#region Copyright (c) 2003-2004 ComponentGo!
/*
*********************************************************************
*                                                                   *
*       ComponentGo! Component Library                              *
*                                                                   *
*       Copyright (c) 2003-2004 ComponentGo!                        *
*       ALL RIGHTS RESERVED                                         *
*                                                                   *
*********************************************************************
*/
#endregion

using System;
using System.Runtime.Serialization;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;

namespace ComponentGo.Calendars {

	/// <summary>
	/// Daily calendar control. Principal control of the library. Can be placed on a form.
	/// </summary>
	/// <remarks>
	/// Daily calendar is a principal form of the library. Displays appointments in days 
	/// and hours table. An appointment list is managed with 
	/// an <see cref="RootCalendar.Appointments"/> property. A property returns an object belonging
	/// to a collection. This object has methods to add new or remove existing <see cref="Appointment"/>
	/// objects.<para/>
	/// Properties <see cref="FirstDate"/>, <see cref="FirstDate"/> and <see cref="VisibleDays"/>
	/// are responsible for a day and a range of visible hours. <para/>
	/// Editing appointments belonging to a calendar is also possible.
	/// The events <see cref="RootCalendar.Editing"/>, <see cref="RootCalendar.Edited"/> 
	/// and <see cref="RootCalendar.EnableEditInPlace"/> are used to control the process of editing.
	/// </remarks>
	[Designer(typeof(Design.BaseCalendarDesigner), typeof(System.ComponentModel.Design.IDesigner))]
	[ToolboxItem(true)]
	[System.Runtime.InteropServices.ComVisible(true)]
	[System.Runtime.InteropServices.Guid("AFFCA82A-4BDD-4f48-A03D-60A35A15018C")]
	public class DailyCalendar : ComponentGo.Calendars.BaseCalendar {
		
		#region Types

		/// <summary>
		/// Contains the values indentifying various regions of the control.
		/// </summary>
		/// <remarks>
		/// Used by various methods to determine a region (rectangle), the control 
		/// is inquired about
		/// <seealso cref="GetDayRectangle"/>
		/// <seealso cref="HitTest"/>
		/// </remarks>
		public enum Regions { 
			/// <summary>
			/// Region containing a 'bar' with numbers representing hours. 
			/// This 'bar' is located on the left side of the calendar.
			/// </summary>
			HoursLabel, 

			/// <summary>
			/// A region representing a bar with texts describing each resource.
			/// </summary>
			ResourcesLabel, 

			/// <summary>
			/// Day label containing a day or days located in an upper 
			/// portion of the control.
			/// </summary>
			DaysLabel, 

			/// <summary>
			/// Working area containing appoinments' rectangles 
			/// laid out with hourly resolution.
			/// </summary>
			HoursWorkspace, 

			/// <summary>
			/// Region containing appoinments' rectangles 
			/// laid out with daily resolution, located in an upper portion of 
			/// the control.
			/// </summary>
			DaysWorkspace, 
		}

		#endregion

		#region General

		/// <summary>
		/// Constructor creating a calendar control. 
		/// Properties are initialized to standard values.
		/// </summary>
		public DailyCalendar() : this(null) {
		}

		internal DailyCalendar(RootCalendar root) : base(root, true, true) {
			Size = new Size(150, 200);
		}

		#endregion

		#region Properties

		#region HoursLabelColor

		Color hoursLabelColor = SystemColors.Control;
		Color hoursLabelColorSecond = SystemColors.Control;

		/// <summary>
		/// A color of an hour bar.
		/// </summary>
		/// <remarks>
		/// An hour bar is located in a left portion of a control.
		/// </remarks>
		[Category("Colors")]
		[Description("A color of an hour bar")]
		public Color HoursLabelColor {
			get { return hoursLabelColor; }
			set {
				if (hoursLabelColor==value) return;
				if (hoursLabelColor==hoursLabelColorSecond)
					hoursLabelColorSecond = value;
				hoursLabelColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="HoursLabelColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeHoursLabelColor() {
			return hoursLabelColor!=SystemColors.Control;
		}

		/// <summary>
		/// Second color for a <see cref="HoursLabelColor"/> property used 
		/// for a linear gradient
		/// </summary>
		[Category("Colors")]
		[Description("Second color for an hour bar used for a linear gradient")]
		public Color HoursLabelColorSecond {
			get { return hoursLabelColorSecond; }
			set {
				if (hoursLabelColorSecond==value) return;
				hoursLabelColorSecond = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="HoursLabelColorSecond"/> property 
		/// should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeHoursLabelColorSecond() {
			return hoursLabelColor!=hoursLabelColorSecond;
		}

		#endregion

		#region DaysLabelColor

		Color daysLabelColor = SystemColors.Control;
		Color daysLabelColorSecond = SystemColors.Control;

		/// <summary>
		/// Day label color.
		/// </summary>
		/// <remarks>
		/// Day label is located in the upper portion of a control.
		/// </remarks>
		[Category("Colors")]
		[Description("Day label color")]
		public Color DaysLabelColor {
			get { return daysLabelColor; }
			set {
				if (daysLabelColor==value) return;
				if (daysLabelColor==daysLabelColorSecond)
					daysLabelColorSecond = value;
				daysLabelColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="DaysLabelColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeDaysLabelColor() {
			return daysLabelColor!=SystemColors.Control;
		}

		/// <summary>
		/// Second color for a <see cref="DaysLabelColor"/> property used for linear gradient
		/// </summary>
		[Category("Colors")]
		[Description("Second color for a day label property used for linear gradient")]
		public Color DaysLabelColorSecond {
			get { return daysLabelColorSecond; }
			set {
				if (daysLabelColorSecond==value) return;
				daysLabelColorSecond = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="DaysLabelColorSecond"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeDaysLabelColorSecond() {
			return daysLabelColor!=daysLabelColorSecond;
		}

		#endregion

		#region ResourcesLabelColor

		Color resourcesLabelColor = SystemColors.Control;
		Color resourcesLabelColorSecond = SystemColors.Control;

		/// <summary>
		/// Resource label color.
		/// </summary>
		/// <remarks>
		/// Resource label is located in the upper portion of a control.
		/// </remarks>
		[Category("Colors")]
		[Description("Resource label color")]
		public Color ResourcesLabelColor {
			get { return resourcesLabelColor; }
			set {
				if (resourcesLabelColor==value) return;
				if (resourcesLabelColor==resourcesLabelColorSecond)
					resourcesLabelColorSecond = value;
				resourcesLabelColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="ResourcesLabelColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeResourcesLabelColor() {
			return resourcesLabelColor!=SystemColors.Control;
		}

		/// <summary>
		/// Second color for a <see cref="ResourcesLabelColor"/> property used for linear gradient
		/// </summary>
		[Category("Colors")]
		[Description("Second color for a resource label property used for linear gradient")]
		public Color ResourcesLabelColorSecond {
			get { return resourcesLabelColorSecond; }
			set {
				if (resourcesLabelColorSecond==value) return;
				resourcesLabelColorSecond = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="ResourcesLabelColorSecond"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeResourcesLabelColorSecond() {
			return resourcesLabelColor!=resourcesLabelColorSecond;
		}

		#endregion

		#region DaysWorkspaceColor

		Color daysWorkspaceColor = SystemColors.ControlDark;
		Color daysWorkspaceColorSecond = SystemColors.ControlDark;

		/// <summary>
		/// Background color for a day located it an upper portion of a calendar, 
		/// over the hours.
		/// </summary>
		[Category("Colors")]
		[Description("Background color for a day located it an upper portion of a calendar, over the hours.")]
		public Color DaysWorkspaceColor {
			get { return daysWorkspaceColor; }
			set {
				if (daysWorkspaceColor==value) return;
				if (daysWorkspaceColor==daysWorkspaceColorSecond)
					daysWorkspaceColorSecond = value;
				daysWorkspaceColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="DaysWorkspaceColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeDaysWorkspaceColor() {
			return daysWorkspaceColor!=SystemColors.ControlDark;
		}

		/// <summary>
		/// Second color for a <see cref="DaysWorkspaceColor"/> property used 
		/// for a linear gradient
		/// </summary>
		[Category("Colors")]
		[Description("Second color for a day property used for a linear gradient")]
		public Color DaysWorkspaceColorSecond {
			get { return daysWorkspaceColorSecond; }
			set {
				if (daysWorkspaceColorSecond==value) return;
				daysWorkspaceColorSecond = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="DaysWorkspaceColorSecond"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.
		/// </returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeDaysWorkspaceColorSecond() {
			return daysWorkspaceColor!=daysWorkspaceColorSecond;
		}

		Color selectedDayColor = SystemColors.Window;

		/// <summary>
		/// Selected day color.
		/// </summary>
		[Category("Colors")]
		[Description("Selected day color")]
		public Color SelectedDayColor {
			get { return selectedDayColor; }
			set {
				if (selectedDayColor==value) return;
				selectedDayColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="SelectedDayColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeSelectedDayColor() {
			return selectedDayColor!=SystemColors.Window;
		}

		#endregion

		#region VisibleColumns

		/// <summary>
		/// An event raised, after a <see cref="VisibleColumns"/> property is changed.
		/// </summary>
		/// <remarks>
		/// A <see cref="VisibleColumnsChanged"/> event should be used instead.
		/// </remarks>
		[Obsolete]
		[Browsable(false)]
		public event EventHandler VisibleDaysChanged {
			add { VisibleColumnsChanged += value; }
			remove { VisibleColumnsChanged += value; }
		}

		/// <summary>
		/// An event raised, after a <see cref="VisibleColumns"/> property is changed.
		/// </summary>
		[Category("Property Changed")]
		[Description("An event raised, after a VisibleColumns property is changed")]
		public event EventHandler VisibleColumnsChanged;

		/// <summary>
		///	Raises the <see cref="VisibleColumnsChanged"/> event.
		/// </summary>
		/// <param name="args">An <see cref="EventArgs"/> that contains the event data.</param>
		protected virtual void OnVisibleColumnsChanged(EventArgs args) {
			if (VisibleColumnsChanged!=null)
				VisibleColumnsChanged(this, args);
		}

		int visibleColumns = 1;

		/// <summary>
		/// A number of days visible in a calendar.
		/// </summary>
		/// <remarks>
		/// A <see cref="VisibleColumns"/> property should be used instead.<para/>
		/// Determines a number of calendar columns, where each column represents one day.
		/// <para/>
		/// First visible day can be set with a <see cref="FirstDate"/> property.
		/// <seealso cref="VisibleHours"/>
		/// <seealso cref="VisibleColumns"/>
		/// </remarks>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		[Obsolete]
		public int VisibleDays {
			get { return VisibleColumns; }
			set { VisibleColumns = value; }
		}

		/// <summary>
		/// A number of columns visible in a calendar.
		/// </summary>
		/// <remarks>
		/// Determines a number of calendar columns, where each column represents one category at a day.
		/// <para/>
		/// First visible day can be set with a <see cref="FirstDate"/> property.
		/// First visible category can be set with a <see cref="FirstResource"/> property.
		/// <seealso cref="VisibleHours"/>
		/// </remarks>
		[Category("Behavior")]
		[Description("A number of columns displayed simultaneously in a calendar.")]
		[DefaultValue(1)]
		public int VisibleColumns {
			get { return visibleColumns; }
			set {
				if (visibleColumns==value) return;
				if (value<1) 
					throw new ArgumentException();
				visibleColumns = value;
				OnVisibleColumnsChanged(EventArgs.Empty);
				Resync(true);
			}
		}

		#endregion

		#region FirstHour

		/// <summary>
		/// An event raized after the <see cref="FirstHour"/> property change.
		/// </summary>
		[Category("Property Changed")]
		[Description("An event raized after the FirstHour property change")]
		public event EventHandler FirstHourChanged;

		/// <summary>
		///	Raises the <see cref="FirstHourChanged"/> event.
		/// </summary>
		/// <param name="args">An <see cref="EventArgs"/> that contains the event data.</param>
		protected virtual void OnFirstHourChanged(EventArgs args) {
			if (FirstHourChanged!=null)
				FirstHourChanged(this, args);
		}

		static TimeSpan defaultHour = new TimeSpan(7, 0, 0);
		TimeSpan firstHour = defaultHour;

		/// <summary>
		/// An hour displayed as the first (first from the top).
		/// </summary>
		/// <remarks>
		/// The number of displayed hours depends  on its height and
		/// <see cref="BaseCalendar.HoursResolution"/> property.
		/// </remarks>
		[Category("Data")]
		[Description("An hour displayed as the first (first from the top).")]
		public TimeSpan FirstHour {
			get { return firstHour; }
			set {
				value = round(value, false);
				if (firstHour==value) return;
				if (!stopEdit(false)) return;
				firstHour = value;
				OnFirstHourChanged(EventArgs.Empty);
				initScrollBars();
				Resync(true);
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="FirstHour"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeFirstHour() {
			return FirstHour!=defaultHour;
		}

		/// <summary>
		/// Resets a <see cref="FirstHour"/> property to a default value.
		/// </summary>
		public void ResetFirstHour() {
			FirstHour = defaultHour;
		}

		#endregion

		#region Align

		ContentAlignment textAlign = ContentAlignment.TopLeft;

		/// <summary>
		/// Gets or sets the alignment of text in the appointment box.
		/// </summary>
		/// <value>
		/// One of the <see cref="ContentAlignment"/> values. The default is 
		/// <see cref="ContentAlignment.TopLeft"/>.
		/// </value>
		[DefaultValue(ContentAlignment.TopLeft)]
		[Category("Appearance")]
		[Description("Gets or sets the alignment of text in the appointment box.")]
		public ContentAlignment TextAlign {
			get {
				return textAlign;
			}
			set {
				if (textAlign==value) return;
				textAlign = value;
				Invalidate();
			}
		}

		ContentAlignment textAllDayAlign = ContentAlignment.MiddleCenter;

		/// <summary>
		/// Gets or sets the alignment of text in the all day appointment box.
		/// </summary>
		/// <value>
		/// One of the <see cref="ContentAlignment"/> values. The default is 
		/// <see cref="ContentAlignment.MiddleCenter"/>.
		/// </value>
		[DefaultValue(ContentAlignment.MiddleCenter)]
		[Category("Appearance")]
		[Description("Gets or sets the alignment of text in an all-day appointment box.")]
		public ContentAlignment TextAllDayAlign {
			get {
				return textAllDayAlign;
			}
			set {
				if (textAllDayAlign==value) return;
				textAllDayAlign = value;
				Invalidate();
			}
		}

		ContentAlignment textHeaderAlign = ContentAlignment.MiddleCenter;

		/// <summary>
		/// Gets or sets the alignment of text in resources and days label boxes.
		/// </summary>
		/// <value>
		/// One of the <see cref="ContentAlignment"/> values. The default is 
		/// <see cref="ContentAlignment.MiddleCenter"/>.
		/// </value>
		[DefaultValue(ContentAlignment.MiddleCenter)]
		[Category("Appearance")]
		[Description("Gets or sets the alignment of text in resources and days label boxes.")]
		public ContentAlignment TextHeaderAlign {
			get {
				return textHeaderAlign;
			}
			set {
				if (textHeaderAlign==value) return;
				textHeaderAlign = value;
				Invalidate();
			}
		}

		#endregion

		#region AllowDateChange

		bool allowDateChange = true;

		/// <summary>
		/// Allows for changing a current date while a control is being used.
		/// </summary>
		/// <remarks>
		/// A standard value of <b>true</b> allows the operator to change current date.
		/// Setting a value to <b>false</b> hides a horizontal scrollbar an disallows the operator
		/// to change a date using arrow keys
		/// </remarks>
		[DefaultValue(true)]
		[Category("Behavior")]
		[Description("Allows for changing a current date while a control is being used.")]
		public bool AllowDateChange {
			get { return allowDateChange; }
			set {
				if (allowDateChange==value) return;
				allowDateChange = value;
				ScrollBars = (ScrollBars & ScrollBars.Vertical)
					| (allowDateChange ? ScrollBars.Horizontal : ScrollBars.None);
			}
		}

		#endregion

		#region AllDayVisible

		bool allDayVisible = true;

		/// <summary>
		/// Display a rectangle containing all-day appointments in an upper portion of a control.
		/// </summary>
		/// <remarks>
		/// A standard value is <b>true</b>.
		/// Setting a value to <b>false</b> hides the upper part of a control responsible for
		/// displayin all-day appointments. They will be displayed in a main portion
		/// of a calendar.
		/// </remarks>
		[DefaultValue(true)]
		[Category("Appearance")]
		[Description("Display a rectangle containing all-day appointments in an upper portion of a control.")]
		public bool AllDayVisible {
			get { return allDayVisible; }
			set {
				if (allDayVisible==value) return;
				allDayVisible = value;
				Resync(true);
			}
		}

		#endregion

		#region ShowFullDay

		bool showFullDay = false;

		/// <summary>
		/// Forces displaying a day on a calendar starting always at the first resource.
		/// </summary>
		/// <remarks>
		/// Setting a value to <b>true</b> forces display of the first (with index 0) 
		/// resource. In such a case a property 
		/// <see cref="VisibleColumns"/>  value should be a multiple of a number of resources
		/// in a collection  <see cref="RootCalendar.Resources"/>.
		/// </remarks>
		[DefaultValue(false)]
		[Category("Appearance")]
		[Description("Forces displaying a day on a calendar starting always at the first resource.")]
		public bool ShowFullDay {
			get { return showFullDay; }
			set {
				if (showFullDay==value) return;
				showFullDay = value;
				initScrollBars();
			}
		}

		#endregion

		DateTime firstDate = DateTime.Today;

		/// <summary>
		/// The first date visible in a calendar.
		/// </summary>
		/// <remarks>
		/// In case of many dates, this is the first day from the left visible in a calendar.
		/// The number of visible days is controlled by a <see cref="VisibleColumns"/> property.
		/// </remarks>
		[Category("Data")]
		[Description("Date displayed in a calendar. If it is more than one day, the first displayed day.")]
		public DateTime FirstDate {
			get { return firstDate; }
			set {
				if (firstDate==value) return;
				if (value.TimeOfDay!=TimeSpan.Zero)
					throw new ArgumentException("Time part of FirstDate property is not allowed.");
				if (!stopEdit(false)) return;
				firstDate = value;
				Resync(true);
				OnFirstDateChanged(EventArgs.Empty);
				initScrollBars();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="FirstDate"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeFirstDate() {
			return FirstDate!=DateTime.Today;
		}


		int firstResource = 0;

		/// <summary>
		/// The first resource visible in a calendar.
		/// </summary>
		/// <remarks>
		/// In case of many dates and resources, this is the first resource from the left visible in a calendar.
		/// The number of visible days is controlled by a <see cref="VisibleColumns"/> property.
		/// </remarks>
		[Category("Data")]
		[Description("Resource displayed in a calendar. If it is more than one day and resource, the first displayed from the left.")]
		[DefaultValue(0)]
		public int FirstResource {
			get { return firstResource; }
			set {
				if (firstResource==value) return;
				if (!stopEdit(false)) return;
				firstResource = value;
				Resync(true);
				initScrollBars();
			}
		}

		void setFirst(DateTime dt, int r, bool withScrollbars) {
			if (FirstDate==dt && FirstResource==r) return;
			if (!stopEdit(false)) return;
			firstResource = r;
			firstDate = dt;
			Resync(true);
			OnFirstDateChanged(EventArgs.Empty);
			if (withScrollbars)
				initScrollBars();
		}

		internal override DateTime VisibleFirstDateTime {
			get { return FirstDate; }
			set { FirstDate = value.Date; }
		}

		internal override TimeSpan VisibleTimeSpan {
			get { return TimeSpan.FromDays(VisibleColumns / ResCount); }
			set { VisibleColumns = (int)value.TotalDays*ResCount; }
		}

		string dateLabelFormat = "{0:m}";

		/// <summary>
		/// Day display format in the upper portion of a calendar.
		/// </summary>
		/// <remarks>
		/// Standard property value {0:m} means that a day label consists of a day number 
		/// and a full month name. The property value is passed to a 
		/// <see cref="String.Format">String.Format</see> method,
		/// formatting a displayed text. The second parameter is a  <see cref="DateTime"/> 
		/// class object denoting a displayed day.
		/// </remarks>
		/// <example>
		/// An example shows setting a property to display a full date with a string
		/// <b>day</b> at the end.
		/// <code>
		/// dailyCalendar.DateLabelFormat = "{0} day";
		/// </code>
		/// </example>
		[DefaultValue("{0:m}")]
		[Category("Appearance")]
		[Description("Day display format in the upper portion of a calendar")]
		public string DateLabelFormat {
			get { return dateLabelFormat; }
			set {
				if (dateLabelFormat==value) return;
				dateLabelFormat = value;
				Invalidate();
			}
		}

		/// <summary>
		///	Raises the <see cref="BaseCalendar.HoursResolutionChanged"/> event.
		/// </summary>
		/// <remarks>
		/// Contains implementation specific to a DailyCalendar control.
		/// </remarks>
		/// <param name="args">An <see cref="EventArgs"/> that contains the event data.</param>
		protected override void OnHoursResolutionChanged(EventArgs args) {
			base.OnHoursResolutionChanged(args);
			FirstHour = FirstHour;
		}

		int minDayWidth = 150;

		/// <summary>
		/// Denotes a minimum witdth of a day. After it is reached,
		/// the number of days in a calendar is decreased.
		/// </summary>
		/// <remarks>
		/// The standard value of a property is 150.
		/// </remarks>
		[DefaultValue(150)]
		[Category("Behavior")]
		[Description("Denotes a minimum witdth of a day. After it is reached, the number of days in a calendar is decreased.")]
		public int MinDayWidth {
			get { return minDayWidth; }
			set { minDayWidth = value; }
		}

		#endregion

		#region Rectangles

		internal const int markWidth = 6;
		const int dayTaskHeight = 21;
		const int hourTaskHeight = 19;

		int daysLabelHeight = 20;

		bool isResourceBar {
			get {
				return Resources.Count>0;
			}
		}

		int hourItemCount {
			get {
				int h = Height;
				h -= daysLabelHeight;
				if (isResourceBar)
					h -= daysLabelHeight;
				if (AllDayVisible)
					h -= Math.Max(DayTaskCount, 1) * dayTaskHeight;
				h -= HorizontalScrollBarHeight;
				int count = h/hourTaskHeight;
				int n = (int)CalendarTools.Div(new TimeSpan(1, 0, 0, 0), minsResolution);
				return Math.Max(0, Math.Min(count, n));
			}
		}

		int hoursLabelWidth {
			get {
				if (HoursResolution==HoursResolutions.Second) return 65;
				return 50;
			}
		}

		/// <summary>
		/// Read-only property returning information on time displayed in a calendar.
		/// </summary>
		/// <remarks>
		/// A difference between the last and the first hour displayed in a calendar. 
		/// <seealso cref="VisibleDays"/>
		/// </remarks>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public TimeSpan VisibleHours {
			get {
				return CalendarTools.Mul(minsResolution, hourItemCount);
			}
		}

		int clientWidth {
			get { return Width-VerticalScrollBarWidth; }
		}

		/// <summary>
		/// Calculates a rectangle covering the selected region of a control.
		/// </summary>
		/// <param name="region">Region, the rectangle is calculated for.</param>
		/// <returns>Rectangle calculated relative to a calendar control.</returns>
		public Rectangle GetRegionRectangle(Regions region) {
			switch (region) {
				case Regions.HoursLabel:
					return new Rectangle(
						Point.Empty.X, Point.Empty.Y , 
						hoursLabelWidth, Height-HorizontalScrollBarHeight);

				case Regions.DaysLabel:
					return new Rectangle(
						hoursLabelWidth, 0,
						Math.Max(0, clientWidth-hoursLabelWidth), daysLabelHeight);

				case Regions.ResourcesLabel:
					if (isResourceBar)
						return new Rectangle(
							hoursLabelWidth, daysLabelHeight,
							Math.Max(0, clientWidth-hoursLabelWidth), daysLabelHeight);
					return Rectangle.Empty;

				case Regions.HoursWorkspace: {
					int h = hourItemCount*hourTaskHeight;
					return new Rectangle(
						new Point(hoursLabelWidth, Height-h-HorizontalScrollBarHeight), 
						new Size(Math.Max(0, clientWidth-hoursLabelWidth), h));
				}

				case Regions.DaysWorkspace:
					return AllDayVisible ? getDaysWorkspaceRt() : Rectangle.Empty;
			}	
			throw new ArgumentException("Unexpected value", "region");		
		}

		Rectangle getDaysWorkspaceRt() {
			int h = hourItemCount*hourTaskHeight;
			int h2 = isResourceBar ? daysLabelHeight : 0;
			return new Rectangle(
				hoursLabelWidth, 
				daysLabelHeight+h2, 
				Math.Max(0, clientWidth-hoursLabelWidth), 
				Math.Max(0, Height-h-h2-daysLabelHeight-HorizontalScrollBarHeight));
		}

		/// <summary>
		/// Calculates a rectangle containing a selected hour.
		/// </summary>
		/// <param name="hour">An hour, the rectangle is calculated for.</param>
		/// <param name="inner">A value of <b>true</b> allows only for calculation of rectangles
		/// for hours visible on a control. Otherwise <see cref="Rectangle.Empty"/> is returned.
		/// </param>
		/// <param name="roundToRes">Forces alignment of the rectangle 
		/// with  frames drawn on a calendar.
		/// </param>
		/// <returns>Rectangle calculated relative to a calendar control
		/// or <see cref="Rectangle.Empty"/> if the <b>inner</b> condition is satisfied.
		/// </returns>
		public Rectangle GetHourRectangle(TimeSpan hour, bool inner, bool roundToRes) {
			hour -= FirstHour;
			if (inner && hour<TimeSpan.Zero) return Rectangle.Empty;
			int mins = (int)CalendarTools.Div(hour, minsResolution);

			int n = hourItemCount;
			if (inner && mins>=n) return Rectangle.Empty;

			return new Rectangle(
				0, Height-(n-mins)*hourTaskHeight-HorizontalScrollBarHeight, 
				clientWidth, hourTaskHeight);
		}

		/// <summary>
		/// Calculates a Y coordinate for a cell representing a given hour.
		/// </summary>
		/// <param name="hour">An hour the Y coordinate is requested for.</param>
		/// <returns>Returns a coordinate relative to an upper frame of a calendar control.
		/// </returns>
		public int GetHourTopPoint(TimeSpan hour) {
			hour -= FirstHour;
			double mins = CalendarTools.Div(hour, minsResolution);

			int n = hourItemCount;

			return (int)(Height-(n-mins)*hourTaskHeight-HorizontalScrollBarHeight)-1;
		}

		/// <summary>
		/// Rectangle on a control corresponding to one day's region on a calendar.
		/// </summary>
		/// <param name="date">A date in a calendar.</param>
		/// <param name="region">Determines a region of a day to be returned.</param>
		/// <returns>
		/// Rectangle calculated relative to a left upper corner of a control.
		///	</returns>
		///	<exception cref="ArgumentException">Raised when a region is assigned 
		///	an unexpected value <see cref="Regions.HoursLabel">Regions.HoursLabel</see>.</exception>
		public Rectangle GetDayRectangle(DateTime date, Regions region) {
			if (region==Regions.HoursLabel)
				throw new ArgumentException("Unexpected region.", "region");

			return getDayRectangle(GetRegionRectangle(region), date);
		}

		/// <summary>
		/// Rectangle on a control, corresponding to one day on a calendar.
		/// </summary>
		/// <param name="date">A date in a calendar.</param>
		/// <returns>
		/// Rectangle calculated relative to a left upper corner of a calendar control.
		/// </returns>
		public Rectangle GetDayRectangle(DateTime date) {
			Rectangle rt = GetRegionRectangle(Regions.HoursLabel);
			rt.X = rt.Right;
			rt.Width = clientWidth-rt.X;

			return getDayRectangle(rt, date);
		}

		/// <summary>
		/// Rectangle on a control, corresponding to one resource in a day on a calendar.
		/// </summary>
		/// <param name="date">A date in a calendar.</param>
		/// <param name="resourceIndex">A resource index in a calendar.</param>
		/// <returns>
		/// Rectangle calculated relative to a left upper corner of a calendar control.
		/// </returns>
		public Rectangle GetResourceRectangle(DateTime date, int resourceIndex) {
			Rectangle rt = GetRegionRectangle(Regions.HoursLabel);
			rt.X = rt.Right;
			rt.Width = clientWidth-rt.X;

			return getResourceRectangle(rt, date, resourceIndex);
		}

		Rectangle getDayRectangle(Rectangle rt, DateTime date) {
			return getColumnsRectangle(rt, GetColumn(date, 0), GetColumn(date.AddDays(1), 0));
		}

		Rectangle getResourceRectangle(Rectangle rt, DateTime date, int resourceIndex) {
			int column = GetColumn(date, resourceIndex);
			return getColumnsRectangle(rt, column, column+1);
		}

		Rectangle getColumnsRectangle(Rectangle rt, int from, int to) {
			int ncols = VisibleColumns;
			int colWidth = rt.Width/ncols;
			int x1 = from<=0 ? rt.Left : rt.Left+colWidth*from;
			int x2 = to>=ncols ? rt.Right : rt.Left+colWidth*to;

			return new Rectangle(x1, rt.Top, x2-x1, rt.Height);
		}

		/// <summary>
		/// Returns a rectangles on a screen containing a given appointment.
		/// </summary>
		/// <param name="ap">Appointment, the rectangles are returned for.</param>
		/// <returns>A table of rectangles, calculated relative to 
		/// an upper left corner of the calendar control. 
		///	</returns>
		///	<remarks>
		///	In case of all-day events the returned table always contains one element.<para/>
		///	In case of objects that can start and end in the middle of a day
		///	the table contains rectangles corresponding to each day of an appoinment.
		///	<seealso cref="Appointment.AllDay"/>
		///	</remarks>
		public override Rectangle [] GetAppointmentRectangles(Appointment ap) {
			if (ap.AllDay && AllDayVisible) 
			{
				if (isResourceBar)
					return getAllDayRt(ap);
				else
					return new Rectangle[] { getAllDayRtNoResource(ap) };
			}
			else
				return getHoursRt(ap);
		}

		int calcFirstLastColumns(Appointment ap, out int firstCol, out DateTime firstDate) {
			firstCol = int.MinValue;
			firstDate = DateTime.MinValue;
			int ncols = VisibleColumns;
			int count = 0;

			for (DateTime d = ap.DateBegin.Date; d<=ap.DateEnd.Date; d = d.AddDays(1)) {
				int col = GetColumn(d, ap.ResourceIndex);
				if (0<=col && col<ncols) {
					if (firstCol==int.MinValue) {
						firstCol = col;
						firstDate = d;
					}
					++count;
				}
				else if (firstCol!=int.MinValue)
					break;
			}

			return count;
		}

		Rectangle getAllDayRtNoResource(Appointment ap) {
			int i = CurrentDays.IndexOf(ap);
			Debug.Assert(i>=0);

			int c1 = Math.Max(0, GetColumn(ap.dateStart, 0));
			int c2 = Math.Min(VisibleColumns, GetColumn(ap.dateStop, 0));

			Rectangle rt = getColumnsRectangle(GetRegionRectangle(Regions.DaysWorkspace), c1, c2);

			return new Rectangle(
				rt.Left+4, rt.Top+2+itemsDays[i].Index*dayTaskHeight, 
				rt.Width-8, dayTaskHeight-3);
		}

		Rectangle [] getAllDayRt(Appointment ap) {
			int idx = CurrentDays.IndexOf(ap);
			Debug.Assert(idx>=0);

			int firstCol;
			DateTime firstDate;
			Rectangle [] rts = new Rectangle[calcFirstLastColumns(ap, out firstCol, out firstDate)];
			if (rts.Length>0) {
				Rectangle workRt = GetRegionRectangle(Regions.DaysWorkspace);

				for (int i = 0; i<rts.Length; ++i) {
					Rectangle rt = getColumnsRectangle(workRt, firstCol, firstCol+1);
					rts[i] = new Rectangle(
						rt.Left+4, rt.Top+2+itemsDays[idx].Index*dayTaskHeight, 
						rt.Width-8, dayTaskHeight-3);

					firstCol += ResCount;
				}
			}

			return rts;
		}

		Rectangle [] getHoursRt(Appointment ap) {
			int firstCol;
			DateTime firstDate;
			Rectangle [] rts = new Rectangle[calcFirstLastColumns(ap, out firstCol, out firstDate)];
			if (rts.Length>0) 
			{
				int startCol = GetColumn(ap.dateStart, ap.ResourceIndex);
				int stopCol = GetColumn(ap.dateStop, ap.ResourceIndex);

				Rectangle workRt = GetRegionRectangle(Regions.HoursWorkspace);

				int apIdx = CurrentHours.IndexOf(ap);
				if (apIdx<0) return new Rectangle[0];

				for (int i = 0; i<rts.Length; ++i) 
				{
					DateTime d1 = firstDate.AddDays(i);
					if (d1<ap.dateStart) d1 = ap.dateStart;
					TimeSpan t1 = d1.TimeOfDay;

					DateTime d2 = firstDate.AddDays(i+1);
					if (d2>ap.dateStop.AddMinutes(-1)+minsResolution) 
						d2 = ap.dateStop.AddMinutes(-1)+minsResolution;
					TimeSpan t2 = d2.TimeOfDay;
					if (t2==TimeSpan.Zero) t2 = TimeSpan.FromDays(1);

					Rectangle rt = Rectangle.Empty;
					if (t1<t2) 
					{
						Rectangle rt1 = GetHourRectangle(t1, false, true);
						if (rt1!=Rectangle.Empty) 
						{
							Rectangle rt2 = GetHourRectangle(t2, false, true);
							if (rt1!=Rectangle.Empty) 
							{
								rt = getColumnsRectangle(workRt, firstCol, firstCol+1);

								int w = rt.Width/Math.Max(itemsHours[apIdx].Count, 1);
								rt.X += w*itemsHours[apIdx].Index;
								rt.Width = w-4;
								rt.Y = rt1.Top-1;
								rt.Height = rt2.Top-rt1.Top;
							}
						}
					}
					//if (ap.Selected) 
					//{
					//	if (firstCol==startCol) 
					//	{
					//		rt.Y -= markWidth;
					//		rt.Height += markWidth;
					//	}
					//	if (firstCol==stopCol)
					//		rt.Height += markWidth;
					//}
					rts[i] = rt;
					firstCol += ResCount;
				}
			}
			return rts;
		}

		/// <summary>
		/// Calculates a rectangle, containing text located inside an appointment's rectangle.
		/// </summary>
		/// <remarks>
		///	Rectangle of appointment's text is also used as a rectangle of a text editor used to edit the text.
		/// </remarks>
		/// <param name="ap">Appointment a rectangle is calculated for.</param>
		/// <param name="eip">Determines whether a rectangle used by EiP <see cref="BaseCalendar.EditControl"/> 
		/// should be calculated. 
		/// In a <see cref="DailyCalendar"/> control this parameter is ignored.</param>
		/// <returns>A rectangle calculated.</returns>
		public override Rectangle GetAppointmentTextRectangle(Appointment ap, bool eip) {
			Rectangle[] rts = GetAppointmentRectangles(ap);
			if (rts.Length>0)
			{
				Rectangle rt = GetAppointmentRectangles(ap)[0];
				if (ap.AllDay) 
				{
					rt.Inflate(-1, -1);
					rt.X += 1;
					rt.Y += 1;
					rt.Width -= 1;
					rt.Height -= 1;
				}
				else 
				{
					rt.Y += markWidth+1;
					rt.Height -= 2*markWidth+1;
					rt.X += markWidth+1;
					rt.Width -= markWidth+1;
				}

				return rt;
			}
			else
			{
				return Rectangle.Empty;
			}
		}

		int GetColumn(DateTime date, int resourceIndex) {
			int days = (int)(date-FirstDate).TotalDays;
			return days*ResCount-FirstResource+resourceIndex;
		}

		void Increment(ref DateTime date, ref int resourceIndex) {
			if (isResourceBar) {
				if (++resourceIndex>=Resources.Count) {
					resourceIndex = 0;
					date = date.AddDays(1);
				}
			}
			else
				date = date.AddDays(1);
		}

		void Decrement(ref DateTime date, ref int resourceIndex) {
			if (isResourceBar) {
				if (--resourceIndex<0) {
					resourceIndex = Resources.Count-1;
					date = date.AddDays(-1);
				}
			}
			else
				date = date.AddDays(-1);
		}

		#endregion

		#region Scrollbars

		internal override void initScrollBars() {
			////////////////////////////////////////////////////////////////
			// Horizontal ScrollBar
			// 
			int widthWin = 40;

			int x = ResCount*(int)(FirstDate-DateZero).TotalDays + FirstResource;

			if (AllowDateChange) {
				hScroll.Minimum = x-widthWin;
				hScroll.Maximum = x+widthWin+ResCount;
			}
			else {
				hScroll.Minimum = ResCount*(int)(FirstDate-DateZero).TotalDays;
				int days = (VisibleColumns-1)/ResCount+1;
				hScroll.Maximum = hScroll.Minimum+days*ResCount-1;
			}
			hScroll.Value = x;
			hScroll.LargeChange = VisibleColumns;
			hScroll.SmallChange = ShowFullDay ? Resources.Count : 1;

			///////////////////////////////////////////////////////////////
			// Vertical ScrollBar
			// 
			int hic = hourItemCount;
			int mins = (int)minsResolution.TotalSeconds;
			int minH = mins*hic;
			vScroll.Enabled = minH<24*60*60;
			if (vScroll.Enabled) {
				vScroll.Minimum = 0;
				vScroll.Maximum = 24*60*60;
				vScroll.Value = (int)FirstHour.TotalSeconds;
				vScroll.SmallChange = mins;
				vScroll.LargeChange = minH;
				vScroll.Enabled = hic>0 && GetRegionRectangle(Regions.HoursWorkspace).Width>0;
			}

			////////////////////////////////////////////////////////////
			// Check if it does not extend to the next day
			// 
			if ((int)FirstHour.TotalSeconds+minH>24*60*60)
				FirstHour = TimeSpan.FromSeconds(24*60*60-minH);
		}

		internal override void vScroll_Scroll(object sender, ScrollEventArgs args) {
			FirstHour = TimeSpan.FromSeconds(args.NewValue);
		}

		internal override void hScroll_Scroll(object sender, ScrollEventArgs args) {
			if (args.Type!=ScrollEventType.EndScroll) {
				DateTime dt = DateZero.AddDays(args.NewValue / ResCount);
				int resIdx = ShowFullDay ? 0 : args.NewValue % ResCount;
				setFirst(dt, resIdx, false);
			}
			else if (AllowDateChange) {
				int v = hScroll.Value-hScroll.Minimum;
				int w = hScroll.Maximum-hScroll.Minimum;
				bool change = false;
				if (v<w/10) change = true;
				if (v+hScroll.LargeChange>w-w/10) change = true;
				if (change)
					initScrollBars();
			}
		}

		/// <summary>
		/// Forces visibility of a given period in a calendar.
		/// </summary>
		/// <param name="dateFrom">A beginning of a period, that is to be visible in a calendar.</param>
		/// <param name="dateTo">An end of a period, that is to be visible in a calendar.</param>
		/// <param name="resourceIdx">A index of resource, that is to be visible in a calendar.</param>
		/// <remarks>
		/// It is possible that a given period wil not be wholly visible, because a calendar
		/// is too small. In this case a largest possible portion of a period will be visible.
		/// </remarks>
		public override void EnsureVisible(DateTime dateFrom, DateTime dateTo, int resourceIdx) {
			int resourceIdx2 = resourceIdx;
			if (ShowFullDay) {
				resourceIdx = 0;
				resourceIdx2 = Resources.Count-1;
			}
			if (dateTo.TimeOfDay==TimeSpan.Zero)
				dateTo = dateTo.AddDays(-1);

			int columnTo = GetColumn(dateTo.Date, resourceIdx2);
			if (columnTo>=VisibleColumns && GetColumn(dateFrom.Date, resourceIdx)>=VisibleColumns) {
				DateTime d = dateTo.Date;
				int r = resourceIdx2;
				for (int i = VisibleColumns; --i>0;)
					Decrement(ref d, ref r);
				setFirst(d, r, true);
			}
			
			int columnFrom = GetColumn(dateFrom.Date, resourceIdx);
			if (columnFrom<0)
				setFirst(dateFrom.Date, resourceIdx, true);

			if (dateFrom.TimeOfDay!=TimeSpan.Zero || dateTo.TimeOfDay!=TimeSpan.Zero) {
				TimeSpan ts = dateFrom.Date!=dateTo.Date ? TimeSpan.FromDays(1) : dateTo.TimeOfDay;
				if (ts>FirstHour+VisibleHours)
					FirstHour = ts-VisibleHours;
			
				if (dateFrom.TimeOfDay<FirstHour)
					FirstHour = dateFrom.TimeOfDay;
			}
		}

		#endregion

		#region Appointments

		struct CurrentItem {
			public int Index;
			public int Count;
		}

		ArrayList currentHours = null;
		ArrayList currentDays = null;
		CurrentItem [] itemsHours = null;
		CurrentItem [] itemsDays = null;
		int dayTaskCount = -1;

		internal override RequestAppointmentsEventArgs CreateRequestAppointmentsArgs() {
			int save = dayTaskCount;
			try {
				// Protection before recurrention
				dayTaskCount = 0;
				return new RequestAppointmentsEventArgs(
					FirstDate+FirstHour, FirstDate.AddDays(VisibleColumns-1)+FirstHour+VisibleHours,
					0, int.MaxValue);
			}
			finally {
				dayTaskCount = save;
			}
		}

		static readonly ArrayList EmptyArray = new ArrayList(0);
		
		void initHoursDays() {
			currentHours = currentDays = EmptyArray;
			dayTaskCount = 0;
			if (!Visible) return;

			RequestAppointmentsEventArgs args = forceAppointments();

			if (Appointments.Count!=0) {
				currentHours = new ArrayList();
				currentDays = new ArrayList();
				foreach (Appointment ap in Appointments)
					if (args.Check(ap))
						((ap.AllDay && AllDayVisible) ? currentDays : currentHours).Add(ap);

				///////////////////////////////////////////////////////////////////
				// Initialize items' structures
				// 
				dayTaskCount = itemsInit(currentDays, out itemsDays);
				itemsInit(currentHours, out itemsHours);
			}
		}

		class AppointmentComparer : IComparer {
			public int Compare(object x, object y) {
				Appointment ap1 = (Appointment)x;
				Appointment ap2 = (Appointment)y;
				return ap1.CompareTo(ap2);
			}
		}

		int itemsInit(ArrayList arr, out CurrentItem [] items) {
			arr.Sort(new AppointmentComparer());

			int resCount = ResCount;
			Appointment [][] slots = new Appointment[resCount][];
			for (int i = 0; i<slots.Length; ++i)
				slots[i] = new Appointment[1];
			items = new CurrentItem[arr.Count];
			int [] max = new int[resCount], last = new int[resCount], count = new int[resCount];

			for (int idx = 0; idx<arr.Count; ++idx) {
				Appointment a = (Appointment)arr[idx];
				int resIdx = a.ResourceIndex;
				if (resIdx<slots.Length) {
					Appointment [] slot = slots[resIdx];

					//////////////////////////////////////////////////////
					// Remove ended appointents from apps
					// 
					for (int i = 0; i<slot.Length && count[resIdx]>0; ++i) {
						if (slot[i]!=null && round(slot[i].dateStop, true)<=round(a.dateStart, false)) {
							slot[i] = null;
							if (--count[resIdx]==0) {
								int l = last[resIdx];
								for (; l<idx; ++l) {
									Appointment a2 = (Appointment)arr[l];
									if (a2.ResourceIndex==resIdx)
										items[l].Count = max[resIdx];
								}
								last[resIdx] = l;
								max[resIdx] = 0;
							}
						}
					}

					//////////////////////////////////////////////////////
					// Find a free slot for an appointment
					// 
					int index = Array.IndexOf(slot, null);

					/////////////////////////////////////////////////////
					// Append a new slot if needed
					// 
					if (index<0) {
						index = slot.Length;
						Appointment [] newSlots = new Appointment[slot.Length+1];
						slot.CopyTo(newSlots, 0);
						slots[resIdx] = slot = newSlots;
					}

					/////////////////////////////////////////////////////
					// Assign a free slot
					// 
					slot[index] = a;
					items[idx].Index = index;
					if (index>=max[resIdx]) max[resIdx] = index+1;
					++count[resIdx];
				}
			}

			for (int resIdx = 0; resIdx<resCount; ++resIdx) {
				int l = last[resIdx];
				for (; l<arr.Count; ++l) {
					Appointment a = (Appointment)arr[l];
					if (a.ResourceIndex==resIdx)
						items[l].Count = max[resIdx];
				}
			}
			
			int maxCount = slots[0].Length;
			for (int i = 1; i<resCount; ++i)
				if (slots[i].Length>maxCount)
					maxCount = slots[i].Length;

			return maxCount;
		}

		ArrayList CurrentHours {
			get {
				if (currentHours==null)
					initHoursDays();
				return currentHours;
			}
		}

		ArrayList CurrentDays {
			get {
				if (currentDays==null)
					initHoursDays();
				return currentDays;
			}
		}

		int DayTaskCount {
			get {
				if (dayTaskCount==-1)
					initHoursDays();
				return dayTaskCount;
			}
		}

		public override void Resync(bool recalc) {
			if (recalc) {
				currentHours = null;
				currentDays = null;
				dayTaskCount = -1;
			}
			Invalidate();
		}

		#endregion

		#region Paint

		void measures(Graphics g) {
			daysLabelHeight = (int)g.MeasureString("2002-12-31", Font).Height+6;
		}

		#region PaintAppointment

		/// <summary>
		/// Draws an appointment in a selected graphics object.
		/// </summary>
		/// <param name="graphics">Graphics object used to display an appointment.</param>
		/// <param name="ap">An appointment drawn in a calendar. </param>
		public override void Paint(Graphics graphics, Appointment ap) {
			Color textColor = ap.Selected?SelectedAppointmentTextColor:ap.GetForeColor();
			Color busyColor = ap.BusyColor==Color.Empty ? SystemColors.Window : ap.BusyColor;
			Color color = ap.Selected?SelectedAppointmentColor:ap.GetBackColor();
			Color progressColor = ap.ProgressColor;
			if (ap.IsGhost) 
			{
				textColor = Color.FromArgb(102, textColor);
				busyColor = Color.FromArgb(102, busyColor);
				color = Color.FromArgb(102, color);
				progressColor = Color.FromArgb(102, progressColor);
			}

			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl))
			using (Brush textBrush = new SolidBrush(textColor))
			using (Pen linePen = new Pen(ap.AllDay ? ControlPaint.Dark(DaysLabelColorSecond) : ap.GetLineColor()))
			using (SolidBrush busyBrush = new SolidBrush(busyColor))
			using (SolidBrush selectBrush = new SolidBrush(SelectedColor))
			using (SolidBrush brush = new SolidBrush(color)) 
			{
				bool isFirst = GetColumn(ap.DateBegin, ap.ResourceIndex)>=0;
				bool isLast = GetColumn(ap.DateEnd, ap.ResourceIndex)<VisibleColumns;
				if (ap.AllDay) 
				{
					CalendarTools.SetAlignment(textFormat, TextAllDayAlign);
					textFormat.Trimming = StringTrimming.EllipsisCharacter;
					textFormat.FormatFlags |= StringFormatFlags.NoWrap;
				}
				else
					CalendarTools.SetAlignment(textFormat, TextAlign);

				Rectangle [] rects = GetAppointmentRectangles(ap);
				for (int i = 0; i<rects.Length; ++i) 
				{
					Rectangle rt = rects[i];
					 
					Rectangle rect = new Rectangle(rt.Left+5, rt.Top, rt.Width-5, rt.Height);

					if (!ap.AllDay) 
					{
						//////////////////////////////////////////////////////
						// Selected
						// 
						//if (ap.Selected) 
						//{
						//	Rectangle r = new Rectangle(rect.X+5, rect.Y, rect.Width-5, DailyCalendar.markWidth);
						//	if (i==0) 
						//	{
								//graphics.FillRectangle(busyBrush, r);
								//graphics.DrawRectangle(linePen, r);
								//rt.Y += DailyCalendar.markWidth;
								//rt.Height -= DailyCalendar.markWidth;
						//		graphics.FillRectangle(selectBrush, r);
						//		graphics.DrawRectangle(new Pen(SelectedColor),r);  
						//	}
						//	if (i==rects.Length-1) 
						//	{
								//Rectangle r = new Rectangle(rt.X+5, rt.Bottom-DailyCalendar.markWidth, rt.Width-5, DailyCalendar.markWidth);
								//graphics.FillRectangle(busyBrush, r);
								//graphics.DrawRectangle(linePen, r);
								//rt.Height -= DailyCalendar.markWidth;
						//		graphics.FillRectangle(selectBrush, r);
						//		graphics.DrawRectangle(new Pen(SelectedColor),r);  
						//	}
						//}

						//////////////////////////////////////////////////////
						// Busy
						// 
						int y1 = GetHourTopPoint(isFirst && i==0 ? ap.DateBegin.TimeOfDay : TimeSpan.Zero);
						int y2 = GetHourTopPoint(isLast  && i==rects.Length-1 ? ap.DateEnd.TimeOfDay : new TimeSpan(24, 0, 0));
						//graphics.FillRectangle(busyBrush, new Rectangle(
						//	rt.Left+5, y1,
					//		DailyCalendar.markWidth, y2-y1));

						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left, rt.Bottom);
						graphics.DrawLine(linePen, rt.Left+DailyCalendar.markWidth, rt.Top, rt.Left+DailyCalendar.markWidth, rt.Bottom);

						graphics.DrawLine(linePen, rt.Left, y1, rt.Left+DailyCalendar.markWidth, y1);
						graphics.DrawLine(linePen, rt.Left, y2, rt.Left+DailyCalendar.markWidth, y2);

						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left+DailyCalendar.markWidth, rt.Top);
						graphics.DrawLine(linePen, rt.Left, rt.Bottom, rt.Left+DailyCalendar.markWidth, rt.Bottom);

						rt.X += DailyCalendar.markWidth;
						rt.Width -= DailyCalendar.markWidth;
					}

					/////////////////////////////////////////////////////
					// Background of a frame
					// 
					int deltaX = 5;
					if (ap.Progress>0) 
					{
						deltaX = rt.Width*ap.Progress/100;
						using (Brush progressBrush = new SolidBrush(progressColor))
							graphics.FillRectangle(progressBrush, rt.Left, rt.Top, deltaX, rt.Height);
					}
					graphics.FillRectangle(brush, rt.Left+deltaX, rt.Top, rt.Width-deltaX, rt.Height);
					if (ap.AllDay) 
					{
						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Right, rt.Top);
						graphics.DrawLine(linePen, rt.Left, rt.Bottom, rt.Right, rt.Bottom);
						if (isFirst && i==0)
							graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left, rt.Bottom);
						if (isLast && i==rects.Length-1)
							graphics.DrawLine(linePen, rt.Right, rt.Top, rt.Right, rt.Bottom);
					}
					else 
					{						
						graphics.DrawRectangle(linePen, rect);
					}

					//////////////////////////////////////////////////////
					// Text
					// 	
					rt.Inflate(-2, -2);
					Rectangle textRect = new Rectangle(rect.Left+10, rect.Top, rect.Width-10, rect.Height);     
					graphics.DrawString(ap.Text, 
						ap.Font==null ? Font : ap.Font, 
						textBrush, textRect, textFormat);
				}
			}
		}

		#endregion

		#region Hours Label

		void paintHoursLabel(Graphics g) {
			using (Font bigFont = new Font("Arial", HoursResolution==HoursResolutions.One ? 12 : 18))
			using (Font smallFont = new Font("Arial", HoursResolution==HoursResolutions.One ? 7 : 9))
			using (Brush textBrush = new SolidBrush(ForeColor))
			using (Pen linePen = new Pen(ControlPaint.Dark(HoursLabelColorSecond), 1)) {

				bool shortHour = CalendarTools.AMPM;

				Rectangle rt = GetRegionRectangle(Regions.HoursLabel);
				CalendarTools.FillRectangle(g, rt, HoursLabelColor, HoursLabelColorSecond, false);

				////////////////////////////////////////////////
				// Today Marker
				//
				if (TodayMarkerVisible) {
					int y = GetHourTopPoint(DateTime.Now.TimeOfDay);
					Color c = ControlPaint.LightLight(TodayMarkerColor);
					CalendarTools.FillRectangle(g, 
						new Rectangle(rt.Left, y-10, rt.Width, 10), 
						Color.FromArgb(0, c), c, true);

					using (Pen todayPen = new Pen(TodayMarkerColor))
						g.DrawLine(todayPen, rt.Left, y, rt.Right, y);
				}

				TimeSpan mins = minsResolution;
				string lastHH = null, lastS = null;
				int lastY = 0;
				for (int i = hourItemCount; --i>=-1; ) {
					TimeSpan time = CalendarTools.Mul(mins, i)+FirstHour;
					if (time<TimeSpan.Zero) break;

					string smallText = time.Minutes.ToString("00");
					bool isHour, isHalf;
					switch (HoursResolution) {
						case HoursResolutions.Second:
							isHalf = time.Seconds % 5==0;
							isHour = time.Seconds % 15==0;
							smallText = time.Minutes.ToString("00")+':'+time.Seconds.ToString("00");
							break;
						case HoursResolutions.QuarterMinute:
							isHalf = time.Seconds==0;
							isHour = time.Minutes % 5==0 && isHalf;
							break;
						case HoursResolutions.Minute:
							isHour = time.Minutes % 10==0;
							isHalf = time.Minutes % 5==0;
							break;
						default:
							isHour = time.Minutes==0;
							isHalf = time.Minutes==30 && HoursResolution!=HoursResolutions.Half;
							break;
					}
					Rectangle rt2 = GetHourRectangle(time, false, true);
					Debug.Assert(rt2!=Rectangle.Empty);
					if (i==0)
						rt.Height = rt.Top+rt2.Top-1;

					int start = 29;
					if (isHour)
						start = 0;
					else if (isHalf)
						start = 15;
					g.DrawLine(linePen, start, rt2.Top-1, hoursLabelWidth-5, rt2.Top-1);
					if (isHour) {
						int delta = HoursResolution==HoursResolutions.One ? 0 : 2;
						int hour = time.Hours;
						if (shortHour) {
							if (hour==0) hour = 12;
							else if (hour>12) hour -= 12;
						}
						string s = hour.ToString();
						float w = g.MeasureString(s, bigFont).Width;
						g.DrawString(s, bigFont, textBrush, 32-w, rt2.Top+delta);
						if (time>=FirstHour) {
							if (lastHH!=null)
								g.DrawString(lastS, smallFont, textBrush, 27, lastY);
							lastHH = lastS = smallText;
							lastY = rt2.Top+delta+1;
							if (shortHour && time.Minutes==0 && time.Seconds==0) {
								lastHH = (0<=time.Hours && time.Hours<12) ? "am" : "pm";
								if (time.Hours%12==0)
									lastS = lastHH;
							}
						}
					}
					else if (isHalf) {
						g.DrawString(smallText, smallFont, textBrush, 27, rt2.Top+3);
					}
				}
				if (lastHH!=null)
					g.DrawString(lastHH, smallFont, textBrush, 27, lastY);

				TimeSpan time2 = FirstHour+CalendarTools.Mul(mins, hourItemCount);
				bool isHour2 = time2.Minutes==0;

				g.DrawLine(linePen, isHour2 ? 0 : 29, Height-1, hoursLabelWidth-5, Height-1);

				CalendarTools.FillRectangle(g, rt, HoursLabelColor, HoursLabelColorSecond, false);
			}
		}

		#endregion

		#region Days Label

		void paintDaysLabel(Graphics g) {
			Rectangle rt = GetRegionRectangle(Regions.DaysLabel);
			if (rt.Width<=0) return;

			DateTime today = DateTime.Today;

			using (Pen linePen = new Pen(ControlPaint.Dark(DaysLabelColorSecond), 1))
			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl | StringFormatFlags.NoWrap))
			using (Brush textBrush = new SolidBrush(ForeColor)) {
				CalendarTools.SetAlignment(textFormat, TextHeaderAlign);
				textFormat.Trimming = StringTrimming.EllipsisWord;

				for (DateTime date = FirstDate; GetColumn(date, 0)<VisibleColumns; date = date.AddDays(1)) {
					Rectangle dayRt = getDayRectangle(rt, date);
					if (dayRt.Left>rt.Right) break;

					Color secondColor = DaysLabelColorSecond;
					bool isToday = TodayMarkerVisible && date==today;
					if (isToday)
						secondColor = ControlPaint.LightLight(TodayMarkerColor);
					CalendarTools.FillRectangle(g, dayRt, DaysLabelColor, secondColor, true);
					if (isToday)
						using (Pen todayPen = new Pen(TodayMarkerColor))
							g.DrawLine(todayPen, dayRt.Left, dayRt.Bottom-3, dayRt.Right, dayRt.Bottom-3);

					ControlPaint.DrawBorder3D(g, dayRt);

					dayRt.Inflate(-2, -2);
					if (dayRt.Width>0)
						g.DrawString(string.Format(DateLabelFormat, date), 
							Font, textBrush, dayRt, textFormat);
				}
			}
		}

		#endregion

		#region Resources Label

		void paintResourcesLabel(Graphics g) {
			Rectangle rt = GetRegionRectangle(Regions.ResourcesLabel);
			if (rt.Width<=0) return;

			using (Pen linePen = new Pen(ControlPaint.Dark(ResourcesLabelColorSecond), 1))
			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl | StringFormatFlags.NoWrap))
			using (Brush textBrush = new SolidBrush(ForeColor)) {
				CalendarTools.SetAlignment(textFormat, TextHeaderAlign);
				textFormat.Trimming = StringTrimming.EllipsisWord;

				CalendarTools.FillRectangle(g, rt, ResourcesLabelColor, ResourcesLabelColorSecond, true);

				DateTime date = FirstDate;
				int resourceIndex = FirstResource;
				for (; GetColumn(date, resourceIndex)<VisibleColumns; Increment(ref date, ref resourceIndex)) {
					Rectangle dayRt = getResourceRectangle(rt, date, resourceIndex);
					if (dayRt.Left>rt.Right) break;
					ControlPaint.DrawBorder3D(g, dayRt);

					dayRt.Inflate(-2, -2);
					if (dayRt.Width>0)
						g.DrawString(Resources[resourceIndex].Text, 
							Font, textBrush, dayRt, textFormat);
				}
			}
		}

		#endregion

		#region Days Workspace

		void paintDaysWorkspace(Graphics g) {
			Rectangle rt = getDaysWorkspaceRt();
			if (rt.Width<=0 || rt.Height<=0) return;

			using (Brush selectedBrush = new SolidBrush(SelectedDayColor))
			using (Pen linePen = new Pen(ControlPaint.Dark(DaysLabelColorSecond), 1)) {
				CalendarTools.FillRectangle(g, rt, DaysWorkspaceColor, DaysWorkspaceColorSecond, true);
				if (!AllDayVisible) return;

				DateTime date = FirstDate;
				int resourceIdx = FirstResource;
				for (;; Increment(ref date, ref resourceIdx)) {
					Rectangle dayRt = getResourceRectangle(rt, date, resourceIdx);
					if (dayRt.Left>rt.Right) break;
					--dayRt.Width;
					++dayRt.X;
					if (SelectedDateBegin<=date && date.AddDays(1)<=SelectedDateEnd && resourceIdx==SelectedResource)
						g.FillRectangle(selectedBrush, dayRt);
					g.DrawLine(linePen, dayRt.Right, dayRt.Top, dayRt.Right, dayRt.Bottom);
				}

				foreach (Appointment ap in CurrentDays)
					InvokePaintAppointment(new PaintAppointmentEventArgs(g, ap));
			}
		}

		#endregion

		#region Hours Workspace

		void paintHoursWorkspace(Graphics g) 
		{
			Rectangle rect = GetRegionRectangle(Regions.HoursWorkspace);
			if (rect.Width<=0 || rect.Height<=0) return;

			using (Brush selectedBrush = new SolidBrush(SelectedColor))
			using (Pen linePen = new Pen(DayLineColor, 1))
			using (Pen linePenDots = new Pen(DayLineColor, 1)) {
				linePenDots.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
				bool isSel = SelectedDateBegin.TimeOfDay!=TimeSpan.Zero || SelectedDateEnd.TimeOfDay!=TimeSpan.Zero;

				int ncols = VisibleColumns;
				DateTime date = FirstDate;
				int resourceIndex = FirstResource;
				GetDayInfoEventArgs gdi = null;
				GetDayInfoEventArgs.ResourceInfo dayRi = null;
				for (int col = 0; col<ncols; ++col, Increment(ref date, ref resourceIndex)) 
				{
					Rectangle dayRt = getResourceRectangle(rect, date, resourceIndex);
					g.DrawLine(linePen, dayRt.Right-1, dayRt.Top, dayRt.Right-1, dayRt.Bottom);
					Resource res = resourceIndex>=Resources.Count ? null : Resources[resourceIndex];

					if (gdi==null || gdi.Date!=date) 
					{
						gdi = new GetDayInfoEventArgs(this, date);
						OnGetDayInfo(gdi);
						dayRi = new GetDayInfoEventArgs.ResourceInfo(gdi);
					}
					GetDayInfoEventArgs.ResourceInfo ri = null;
					if(gdi.resourcesInfo!=null)
						ri = gdi.resourcesInfo[resourceIndex];
					
					//all hours are work, below - just free hours
					CalendarTools.FillRectangle(g, dayRt, gdi.WorkHourColor, gdi.WorkHourColorSecond, true);

					///////////////////////////////////////////////////////////
					// Calculate a BusyColor
					// 
					Color busyColor = SystemColors.Window;
					try
					{
						for (int i = CurrentDays.Count; --i>=0; ) 
						{
							Appointment ap = (Appointment)CurrentDays[i];
							if (ap.dateStart<=date && date<ap.dateStop && ap.BusyColor!=Color.Empty && ap.ResourceIndex==resourceIndex) 
							{
								busyColor = ap.BusyColor;
								break;
							}
						}
					}
					catch(Exception)
					{
					}
					Color resColor = Color.Empty;
					bool isResColor = res!=null && res.BackColor!=Color.Empty;
					if (isResColor)
						resColor = res.BackColor;
					else if (ri!=null)
						resColor = ri.WorkHourColor;

					if (ri==null) ri = dayRi;

					using (Brush extraBrush = new SolidBrush(ExtraBreakHourColor))
					using (Brush freeBrush = new SolidBrush(ri.FreeHourColor))
					using (Brush busyBrush = new SolidBrush(busyColor)) 
					using (Brush resBrush = resColor==Color.Empty ? null : new SolidBrush(resColor)) {
						TimeSpan mins = minsResolution;
						for (int i = hourItemCount; --i>=0; ) 
						{
							TimeSpan time = FirstHour+CalendarTools.Mul(mins, i);
							DateTime dateTime = date.Add(time);
							bool work = true;
							bool extra = false;
							if (ri!=null)
							{
								work = ri.WorkHourBegin<=time && time<ri.WorkHourEnd; //is it work time or not
								TimeSpan t = time + new TimeSpan(0,1,0,0);								
								if(ri.Breaks.IsBreak(time, t)!=time)
								{
									work = false;
									if(ri.Breaks.IsExtraBreak(time, t))
										extra = true;
									else
										extra = false;
								}
							}
							else
							{
								work = gdi.WorkHourBegin<=time && time<gdi.WorkHourEnd;//is it work time or not
								TimeSpan t = time + new TimeSpan(0,1,0,0);								
								if(ri.Breaks.IsBreak(time, t)!=time)
								{
									work = false;
									if(ri.Breaks.IsExtraBreak(time, t))
										extra = true;
									else
										extra = false;
								}
							}
							if (work && gdi.FreeDay)
								work = false;//is it work time or not

							Rectangle rt = GetHourRectangle(time, true, true);
							rt.Intersect(dayRt);

							bool paint = true;
							Brush backBrush = freeBrush;
							if (isSel && SelectedDateBegin<=dateTime && dateTime<SelectedDateEnd && resourceIndex==SelectedResource)
								backBrush = selectedBrush;
							else if (resBrush!=null && (work || isResColor))
								backBrush = resBrush;
							else if (extra)
								backBrush = extraBrush;
							else if (work)
								paint = false;
							if (paint)
								g.FillRectangle(backBrush, rt); //not work time

							bool isHour = time.Minutes==0;
							g.DrawLine(isHour ? linePen : linePenDots, 
								rt.Left, rt.Bottom-1, rt.Right, rt.Bottom-1);
						}

						g.FillRectangle(busyBrush, new Rectangle( //between columns
							dayRt.Location, new Size(markWidth, dayRt.Height)));
						g.DrawLine(linePen, dayRt.Left, dayRt.Top, dayRt.Left, dayRt.Bottom);
						g.DrawLine(linePen, dayRt.Left+markWidth, dayRt.Top, dayRt.Left+markWidth, dayRt.Bottom);
					}
				}

				foreach (Appointment ap in CurrentHours)
					if (!ap.Selected)
						InvokePaintAppointment(new PaintAppointmentEventArgs(g, ap));
				foreach (Appointment ap in CurrentHours)
					if (ap.Selected)
						InvokePaintAppointment(new PaintAppointmentEventArgs(g, ap));
			}
		}

		#endregion

		#region Links

		void paintLinks(Graphics g) {
			Rectangle rt = GetRegionRectangle(Regions.HoursWorkspace);
			using (Pen pen = new Pen(ControlPaint.Dark(LineColor), 2)) {
				pen.DashStyle = DashStyle.Dash;
				for (int i = 0; i<Appointments.Count-1; ++i) {
					Appointment ap1 = Appointments[i];
					Appointment ap2 = Appointments[i+1];
					if (ap1.LinkWithNext && ap1.dateStop==ap2.dateStart && !ap1.AllDay && !ap2.AllDay) {
						int c;
						int y = GetHourTopPoint(ap2.DateBegin.TimeOfDay);
						if (rt.Top-2<=y && y<=rt.Bottom) {

							if (ap1.ResourceIndex>ap2.ResourceIndex) {
								Appointment a = ap1; ap1 = ap2; ap2 = a;
							}

							int x1;
							c = GetColumn(ap1.dateStop.Date, ap1.ResourceIndex);
							if (c>=VisibleColumns) continue;
							if (c<0)
								x1 = rt.Left;
							else {
								Rectangle [] rtA = GetAppointmentRectangles(ap1);
								if (rtA.Length==0) continue;
								x1 = rtA[rtA.Length-1].Left;
							}

							int x2;
							c = GetColumn(ap2.dateStart.Date, ap2.ResourceIndex);
							if (c<0) continue;
							if (c>=VisibleColumns)
								x2 = rt.Right;
							else {
								Rectangle [] rtA = GetAppointmentRectangles(ap2);
								if (rtA.Length==0) continue;
								x2 = rtA[0].Right;
							}

							g.DrawLine(pen, x1, y, x2, y);
						}
					}
				}
			}
		}
		
		#endregion

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="pe">Like overloaded</param>
		protected override void OnPaint(PaintEventArgs pe) {
			Graphics g = pe.Graphics;
			measures(g);

			paintHoursWorkspace(g);
			paintHoursLabel(g);
			paintDaysLabel(g);
			paintResourcesLabel(g);
			paintDaysWorkspace(g);
			paintLinks(g);
			paintScrollbarSpace(g);

			base.OnPaint(pe);
		}

		#endregion

		#region Hits

		/// <summary>
		/// Calculates a calendar region located at given coordinates.
		/// </summary>
		/// <param name="pt">Coordinates relative to a calendar control
		/// the region is calculated for.</param>
		/// <returns>Calculated region of a calendar control.</returns>
		public Regions HitTest(Point pt) {
			if (GetRegionRectangle(Regions.DaysWorkspace).Contains(pt)) return Regions.DaysWorkspace;
			if (GetRegionRectangle(Regions.ResourcesLabel).Contains(pt)) return Regions.ResourcesLabel;
			if (GetRegionRectangle(Regions.HoursLabel).Contains(pt)) return Regions.HoursLabel;
			if (GetRegionRectangle(Regions.HoursWorkspace).Contains(pt)) return Regions.HoursWorkspace;
			return Regions.DaysLabel;
		}

		DateTime HitDayTest(Point pt) {
			for (DateTime date = FirstDate; ; date = date.AddDays(1)) {
				Rectangle rt = GetDayRectangle(date);
				if (rt.Left>Width) 
					return DateTime.MinValue;
				if (rt.Contains(pt)) 
					return date;
			}
		}

		TimeSpan HitHourTest(Point pt) {
			if (!(0<=pt.X && pt.X<clientWidth)) return TimeSpan.MinValue;

			int n = hourItemCount;
			int y = Height-n*hourTaskHeight-HorizontalScrollBarHeight;
			if (pt.Y<y) return TimeSpan.MinValue;

			y = pt.Y-y;
			y /= hourTaskHeight;

			return FirstHour+CalendarTools.Mul(minsResolution, y);
		}

		/// <summary>
		/// Calculates a day and an hour located at given coordinates.
		/// </summary>
		/// <param name="pt">
		/// Coordinates relative to a calendar control, an hour is calculated for.
		/// </param>
		/// <returns>Structure containing calculated information on day and hour.</returns>
		public override DateTime HitDateTimeTest(Point pt) {
			DateTime date = HitDayTest(pt);
			if (date==DateTime.MinValue) 
				return DateTime.MinValue;

			TimeSpan time = HitHourTest(pt);
			if (time==TimeSpan.MinValue) time = TimeSpan.Zero;
			return date+time;
		}

		/// <summary>
		/// Calculates an index of a resource located at a given point.
		/// </summary>
		/// <param name="pt">Coordinates relative to a calendar control, 
		///		an resource is calculated for.</param>
		/// <returns>An index of a resource.</returns>
		public override int HitResourceTest(Point pt) {
			Rectangle rt = new Rectangle(
				hoursLabelWidth, 0, 
				Width-hoursLabelWidth-VerticalScrollBarWidth,
				Height);
			DateTime date = FirstDate;
			int resourceIndex = FirstResource;
			for (; ; Increment(ref date, ref resourceIndex)) {
				Rectangle dayRt = getResourceRectangle(rt, date, resourceIndex);
				if (dayRt.Contains(pt))
					return resourceIndex;
				if (dayRt.Left>rt.Right) return 0;
			}
		}

		/// <summary>
		/// Check which appointment object is located at given coordinates
		/// </summary>
		/// <param name="pt">Coordinates relative to a calendar control
		/// the appointment is searched for.</param>
		/// <returns>The object of an <see cref="Appointment"/> type located at given 
		/// coordinates or <b>null</b> in case there is no appointment at given coordinates.
		/// </returns>
		public override Appointment HitAppointmentTest(Point pt) {
			bool checkAllDay = HitTest(pt)==Regions.DaysWorkspace;
			if (checkAllDay) {
				foreach (Appointment ap in CurrentDays) {
					if (ap.Selected)
						foreach (Rectangle rt in GetAppointmentRectangles(ap)) {
							if (rt.Contains(pt))
								return ap;
						}
				}
				foreach (Appointment ap in CurrentDays) {
					if (!ap.Selected)
						foreach (Rectangle rt in GetAppointmentRectangles(ap)) {
							if (rt.Contains(pt))
								return ap;
						}
				}
			}
			else {
				if (CurrentHours==null)
					return null;
				foreach (Appointment ap in CurrentHours) {
					if (ap.Selected)
						foreach (Rectangle rt in GetAppointmentRectangles(ap)) {
							if (rt.Contains(pt))
								return ap;
						}
				}
				foreach (Appointment ap in CurrentHours) {
					if (!ap.Selected)
						foreach (Rectangle rt in GetAppointmentRectangles(ap)) {
							if (rt.Contains(pt))
								return ap;
						}
				}
			}
			return null;
		}

		internal override AppRegion HitAppointmentRegion(Appointment ap, Point pt) {
			Rectangle [] rts = GetAppointmentRectangles(ap);

			bool isFirst = GetColumn(ap.DateBegin, ap.ResourceIndex)>=0;
			bool isLast = GetColumn(ap.DateEnd, ap.ResourceIndex)<VisibleColumns;

			for (int i = 0; i<rts.Length; ++i) {
				Rectangle rt = rts[i];
				if (rt.Contains(pt)) {

					if (ap.AllDay) {
						if (isFirst && i==0 && rt.Left<=pt.X && pt.X<rt.Left+3 && ap.dateStart>=FirstDate)
							return AppRegion.BeginMargin;
						if (isLast && i==rts.Length-1 && rt.Right-3<=pt.X && pt.X<rt.Right && ap.dateStop<=FirstDate.AddDays(VisibleColumns))
							return AppRegion.EndMargin;
					}
					else {
						if (rt.X<=pt.X && pt.X<rt.X+markWidth) 
							return AppRegion.Marker;

						int margin = ap.Selected ? markWidth : 3;

						if (rt.Top<=pt.Y && pt.Y<rt.Top+margin && i==0) 
							return AppRegion.BeginMargin;
						if (rt.Bottom-margin<=pt.Y && pt.Y<rt.Bottom && i==rts.Length-1) 
							return AppRegion.EndMargin;
					}
					return AppRegion.Content;

				}
			}
			return AppRegion.None;
		}

		#endregion

		#region Keyboard

		TimeSpan cursorTime {
			get {
				if (SelectedDateBegin.TimeOfDay==TimeSpan.Zero && SelectedDateEnd.TimeOfDay==TimeSpan.Zero)
					return new TimeSpan(1, 0, 0, 0);
				return minsResolution;
			}
		}

		internal override void checkSelected() {
			if (SelectedDateBegin==DateTime.MinValue) {
				SelectedDateBegin = FirstDate.Add(FirstHour);
				SelectedDateEnd = SelectedDateBegin;
			}
		}

		internal override void updateSelection(DateTime dateBegin, DateTime dateEnd, int resource, bool growEnd) {
			if (!AllowDateChange) {
				if (dateBegin.Date<FirstDate) return;

				DateTime d = FirstDate;
				int r = FirstResource;
				for (int i = VisibleColumns; --i>0;)
					Increment(ref d, ref r);
				if (dateEnd>d.AddDays(1)) return;
			}
			base.updateSelection(dateBegin, dateEnd, resource, growEnd);
		}
		
		internal override bool processKey(KeyEventArgs e) {
			int selRes;
			switch (e.KeyData) {
				case Keys.Down:					
				case Keys.Shift | Keys.Down:					
					moveCursor(e.Shift, minsResolution, cursorTime, SelectedResource); 
					return true;
				case Keys.Up:					
				case Keys.Shift | Keys.Up:
					moveCursor(e.Shift, -minsResolution, cursorTime, SelectedResource); 
					return true;
				case Keys.Right:				
				case Keys.Shift | Keys.Right:
					if (addColumnIndex(1, out selRes))
						moveCursor(e.Shift, new TimeSpan(1, 0, 0, 0), cursorTime, selRes); 
					return true;
				case Keys.Left:					
				case Keys.Shift | Keys.Left:					
					if (addColumnIndex(-1, out selRes))
						moveCursor(e.Shift, new TimeSpan(-1, 0, 0, 0), cursorTime, selRes); 
					return true;
				case Keys.Home:					
				case Keys.Shift | Keys.Home:					
					moveCursor(e.Shift, SelectedDateBegin.Date+WorkHourBegin, cursorTime, SelectedResource); 
					return true;
				case Keys.End:					
				case Keys.Shift | Keys.End:					
					moveCursor(e.Shift, SelectedDateEnd.Date+WorkHourEnd-minsResolution, cursorTime, SelectedResource); 
					return true;
				case Keys.Control | Keys.Home:	
				case Keys.Shift | Keys.Control | Keys.Home:	
					moveCursor(e.Shift, SelectedDateBegin.Date, cursorTime, SelectedResource); 
					return true;
				case Keys.Control | Keys.End:	
				case Keys.Shift | Keys.Control | Keys.End:	
					moveCursor(e.Shift, SelectedDateEnd.Date-minsResolution, cursorTime, SelectedResource); 
					return true;
			}
			return false;
		}

		bool addColumnIndex(int delta, out int resourceIndex) {
			resourceIndex = SelectedResource;
			if (!isResourceBar) return true;

			resourceIndex += delta;
			if (resourceIndex>=Resources.Count) {
				resourceIndex = 0;
				return true;
			}
			else if (resourceIndex<0) {
				resourceIndex = Resources.Count-1;
				return true;
			}
			SelectedResource = resourceIndex;
			return false;
		}

		internal override Appointment getNext(Appointment ap) {
			if (ap==null) {
				if (CurrentDays.Count>0)
					return (Appointment)CurrentDays[0];
				if (CurrentHours.Count>0)
					return (Appointment)CurrentHours[0];
			}
			else {
				int i = CurrentDays.IndexOf(ap);
				if (i>=0) {
					++i;
					if (i<CurrentDays.Count)
						return (Appointment)CurrentDays[i];
					i = 0;
				}
				else
					i = CurrentHours.IndexOf(ap)+1;
				if (i<CurrentHours.Count)
					return (Appointment)CurrentHours[i];
			}
			return null;
		}

		internal override Appointment getPrevious(Appointment ap) {
			if (ap==null) {
				if (CurrentHours.Count>0)
					return (Appointment)CurrentHours[CurrentHours.Count-1];
				else if (CurrentDays.Count>0)
					return (Appointment)CurrentDays[CurrentDays.Count-1];
			}
			else {
				int i = CurrentHours.IndexOf(editAppointment);
				if (i>0) 
					return (Appointment)CurrentHours[i-1];

				if (i==0)
					i = CurrentDays.Count;
				else 
					i = CurrentDays.IndexOf(editAppointment);

				if (--i>=0)
					return (Appointment)CurrentDays[i];
			}
			return null;
		}

		#endregion

		#region Mouse

		internal override void mouseClick(System.Windows.Forms.MouseEventArgs e, bool grow) {
			Point pt = new Point(e.X, e.Y);

			/////////////////////////////////////////////////////////////
			// Select hours
			// 
			DateTime dt = HitDayTest(pt);
			if (dt==DateTime.MinValue) return;
			TimeSpan step = new TimeSpan(1, 0, 0, 0);
			int resourceIdx = -1;

			Regions region = HitTest(pt);
			switch (region) {
				case Regions.DaysWorkspace:
				case Regions.ResourcesLabel:
					if (!AllDayVisible) return;
					resourceIdx = HitResourceTest(pt);
					break;

				case Regions.HoursLabel:
				case Regions.HoursWorkspace: {
					TimeSpan time = HitHourTest(pt);
					if (time==TimeSpan.MinValue) return;

					if (region==Regions.HoursWorkspace)
						resourceIdx = HitResourceTest(pt);

					dt += time;
					step = minsResolution;
					break;
				}
			}
			if (resourceIdx<0) resourceIdx = SelectedResource;
			Appointments.UnselectAll();
			moveCursor(grow, dt, step, resourceIdx);
		}

		internal override Cursor getCursorAppearance(Point pt, Appointment ap) {
			return ap.AllDay ? Cursors.SizeWE : Cursors.SizeNS;
		}

		internal override bool recalcPointOnDrag(ref Point pt) {
			if (!AllDayVisible) {
				Regions region = HitTest(pt);
				if (region==Regions.ResourcesLabel || region==Regions.DaysLabel)
					return false;
			}

			if (growAp!=null && !growAp.Appointment.AllDay && growAp.Appointment.Selected)
				if (growAp.Begin)
					pt.Y += markWidth;
				else
					pt.Y -= markWidth;
			return true;
		}

		internal override bool recalcAllDayOnDrag(Point pt, Appointment ap) {
			if (!AllDayVisible) return false;
			Regions region = HitTest(pt);
			return region==Regions.DaysWorkspace || region==Regions.DaysLabel;
		}

		#endregion

		#region -- Custom Printing --

		#region + Printing Process -

		#region - Print - 
		/// <summary>
		/// Performs calendar printing.
		/// </summary>
		/// <param name="graphics"></param>
		public override void Print(Graphics graphics)
		{
			StartPrinting();
			try
			{
				measures(graphics);

				PrintHoursWorkspace(graphics);
				PrintHoursLabel(graphics);
				PrintDaysLabel(graphics);
				PrintResourcesLabel(graphics);
				//paintDaysWorkspace(graphics);
				//paintLinks(graphics);

			}
			finally
			{
				FinishPrinting();
			}
		}
		#endregion //Print

		#region - Print Appointment -
		/// <summary>
		/// Prints the specified appointment.
		/// </summary>
		/// <param name="graphics">The graphics.</param>
		/// <param name="ap">The ap.</param>
		private  void Print(Graphics graphics, Appointment ap) 
		{
			Color textColor = ap.GetForeColor();
			Color busyColor = ap.BusyColor==Color.Empty ? SystemColors.Window : ap.BusyColor;
			Color color = ap.GetBackColor();
			Color progressColor = ap.ProgressColor;
			if (ap.IsGhost) 
			{
				textColor = Color.FromArgb(102, textColor);
				busyColor = Color.FromArgb(102, busyColor);
				color = Color.FromArgb(102, color);
				progressColor = Color.FromArgb(102, progressColor);
			}

			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl))
			using (Brush textBrush = new SolidBrush(textColor))
			using (Pen linePen = new Pen(ap.AllDay ? ControlPaint.Dark(DaysLabelColorSecond) : ap.GetLineColor()))
			using (SolidBrush busyBrush = new SolidBrush(busyColor))
			using (SolidBrush selectBrush = new SolidBrush(SelectedColor))
			using (SolidBrush brush = new SolidBrush(color)) 
			{
				bool isFirst = false;//ap.ResourceIndex>=0;
				bool isLast = false;//GetColumn(ap.DateEnd, ap.ResourceIndex)<VisibleColumns;
				if (ap.AllDay) 
				{
					CalendarTools.SetAlignment(textFormat, TextAllDayAlign);
					textFormat.Trimming = StringTrimming.EllipsisCharacter;
					textFormat.FormatFlags |= StringFormatFlags.NoWrap;
				}
				else
					CalendarTools.SetAlignment(textFormat, TextAlign);

				Rectangle [] rects = GetAppointmentPrintRectangles(ap); 
				for (int i = 0; i<rects.Length; ++i) 
				{
					Rectangle rt = rects[i];
					 
					Rectangle rect = new Rectangle(rt.Left+5, rt.Top, rt.Width-5, rt.Height);

					if (!ap.AllDay) 
					{
						//////////////////////////////////////////////////////
						// Busy
						// 
						int y1 = GetHourTopPrintPoint(isFirst && i==0 ? ap.DateBegin.TimeOfDay : TimeSpan.Zero);
						int y2 = GetHourTopPrintPoint(isLast  && i==rects.Length-1 ? ap.DateEnd.TimeOfDay : new TimeSpan(24, 0, 0));
						//graphics.FillRectangle(busyBrush, new Rectangle(
						//	rt.Left+5, y1,
						//		DailyCalendar.markWidth, y2-y1));

						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left, rt.Bottom);
						graphics.DrawLine(linePen, rt.Left+DailyCalendar.markWidth, rt.Top, rt.Left+DailyCalendar.markWidth, rt.Bottom);

						graphics.DrawLine(linePen, rt.Left, y1, rt.Left+DailyCalendar.markWidth, y1);
						graphics.DrawLine(linePen, rt.Left, y2, rt.Left+DailyCalendar.markWidth, y2);

						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left+DailyCalendar.markWidth, rt.Top);
						graphics.DrawLine(linePen, rt.Left, rt.Bottom, rt.Left+DailyCalendar.markWidth, rt.Bottom);

						rt.X += DailyCalendar.markWidth;
						rt.Width -= DailyCalendar.markWidth;
					}

					/////////////////////////////////////////////////////
					// Background of a frame
					// 
					int deltaX = 5;
					if (ap.Progress>0) 
					{
						deltaX = rt.Width*ap.Progress/100;
						using (Brush progressBrush = new SolidBrush(progressColor))
							graphics.FillRectangle(progressBrush, rt.Left, rt.Top, deltaX, rt.Height);
					}
					graphics.FillRectangle(brush, rt.Left+deltaX, rt.Top, rt.Width-deltaX, rt.Height);
					if (ap.AllDay) 
					{
						graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Right, rt.Top);
						graphics.DrawLine(linePen, rt.Left, rt.Bottom, rt.Right, rt.Bottom);
						if (isFirst && i==0)
							graphics.DrawLine(linePen, rt.Left, rt.Top, rt.Left, rt.Bottom);
						if (isLast && i==rects.Length-1)
							graphics.DrawLine(linePen, rt.Right, rt.Top, rt.Right, rt.Bottom);
					}
					else 
					{						
						graphics.DrawRectangle(linePen, rect);
					}

					//////////////////////////////////////////////////////
					// Text
					// 	
					rt.Inflate(-2, -2);
					Rectangle textRect = new Rectangle(rect.Left+10, rect.Top, rect.Width-10, rect.Height);     
					graphics.DrawString(ap.Text, 
						ap.Font==null ? Font : ap.Font, 
						textBrush, textRect, textFormat);
				}
			}
		}

		#endregion	//	Print Appointment

		#region - PrintResourcesLabel --
		/// <summary>
		/// Prints the resources label.
		/// </summary>
		/// <param name="g">The g.</param>
		private void PrintResourcesLabel(Graphics g) 
		{
			Rectangle rt = GetPrintRegionRectangle(Regions.ResourcesLabel);
			if (rt.Width<=0) return;

			using (Pen linePen = new Pen(ControlPaint.Dark(ResourcesLabelColorSecond), 1))
			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl | StringFormatFlags.NoWrap))
			using (Brush textBrush = new SolidBrush(ForeColor)) 
			{
				CalendarTools.SetAlignment(textFormat, TextHeaderAlign);
				textFormat.Trimming = StringTrimming.EllipsisWord;

				CalendarTools.FillRectangle(g, rt, ResourcesLabelColor, ResourcesLabelColorSecond, true);

				DateTime date = PrintDate;
				for (int resourceIndex = 0; resourceIndex < Resources.Count; resourceIndex++) 
				{
					Rectangle dayRt = new Rectangle(hoursLabelWidth + resourceIndex*ColumnPrintWidth, daysLabelHeight,
							ColumnPrintWidth, daysLabelHeight);
					if (dayRt.Left>rt.Right) break;
					ControlPaint.DrawBorder3D(g, dayRt);

					dayRt.Inflate(-2, -2);
					if (dayRt.Width>0)
						g.DrawString(Resources[resourceIndex].Text, 
							Font, textBrush, dayRt, textFormat);
				}
			}
		}

		#endregion // PrintResourcesLabel

		#region - PrintDayLabel -
		/// <summary>
		/// Prints the days label.
		/// </summary>
		/// <param name="g">The g.</param>
		private void PrintDaysLabel(Graphics g) 
		{
			Rectangle rt = GetPrintRegionRectangle(Regions.DaysLabel);
			if (rt.Width<=0) return;

			DateTime today = DateTime.Today;

			using (Pen linePen = new Pen(ControlPaint.Dark(DaysLabelColorSecond), 1))
			using (StringFormat textFormat = new StringFormat(StringFormatFlags.DisplayFormatControl | StringFormatFlags.NoWrap))
			using (Brush textBrush = new SolidBrush(ForeColor)) 
			{
				CalendarTools.SetAlignment(textFormat, ContentAlignment.MiddleLeft);
				textFormat.Trimming = StringTrimming.EllipsisWord;

				Rectangle dayRt = rt;

				Color secondColor = DaysLabelColorSecond;
				CalendarTools.FillRectangle(g, dayRt, DaysLabelColor, secondColor, true);

				ControlPaint.DrawBorder3D(g, dayRt);

				dayRt.Inflate(-2, -2);
				g.DrawString(string.Format(DateLabelFormat, PrintDate), Font, textBrush, dayRt, textFormat);
			}
		}


		#endregion

		#region - PrintHoursLabel -
		/// <summary>
		/// Prints the hours label.
		/// </summary>
		/// <param name="g">The g.</param>
		private void PrintHoursLabel(Graphics g) 
		{
			using (Font bigFont = new Font("Arial", HoursResolution==HoursResolutions.One ? 12 : 18))
			using (Font smallFont = new Font("Arial", HoursResolution==HoursResolutions.One ? 7 : 9))
			using (Brush textBrush = new SolidBrush(ForeColor))
			using (Pen linePen = new Pen(ControlPaint.Dark(HoursLabelColorSecond), 1)) 
			{

				bool shortHour = CalendarTools.AMPM;

				Rectangle rt = GetPrintRegionRectangle(Regions.HoursLabel);
				CalendarTools.FillRectangle(g, rt, HoursLabelColor, HoursLabelColorSecond, false);

				TimeSpan mins = minsResolution;
				string lastHH = null, lastS = null;
				int lastY = 0;
				for (int i = PrintHourItemCount; --i>=-1; ) 
				{
					TimeSpan time = CalendarTools.Mul(mins, i)+StartPrintTime;
					if (time<TimeSpan.Zero) break;

					string smallText = time.Minutes.ToString("00");
					bool isHour, isHalf;
					switch (HoursResolution) 
					{
						case HoursResolutions.Second:
							isHalf = time.Seconds % 5==0;
							isHour = time.Seconds % 15==0;
							smallText = time.Minutes.ToString("00")+':'+time.Seconds.ToString("00");
							break;
						case HoursResolutions.QuarterMinute:
							isHalf = time.Seconds==0;
							isHour = time.Minutes % 5==0 && isHalf;
							break;
						case HoursResolutions.Minute:
							isHour = time.Minutes % 10==0;
							isHalf = time.Minutes % 5==0;
							break;
						default:
							isHour = time.Minutes==0;
							isHalf = time.Minutes==30 && HoursResolution!=HoursResolutions.Half;
							break;
					}
					Rectangle rt2 = GetHourPrintRectangle(time, false, true);
					Debug.Assert(rt2!=Rectangle.Empty);
					if (i==0)
						rt.Height = rt.Top+rt2.Top-1;

					int start = 29;
					if (isHour)
						start = 0;
					else if (isHalf)
						start = 15;
					g.DrawLine(linePen, start, rt2.Top-1, hoursLabelWidth-5, rt2.Top-1);
					if (isHour) 
					{
						int delta = HoursResolution==HoursResolutions.One ? 0 : 2;
						int hour = time.Hours;
						if (shortHour) 
						{
							if (hour==0) hour = 12;
							else if (hour>12) hour -= 12;
						}
						string s = hour.ToString();
						float w = g.MeasureString(s, bigFont).Width;
						g.DrawString(s, bigFont, textBrush, 32-w, rt2.Top+delta);
						if (time>=StartPrintTime) 
						{
							if (lastHH!=null)
								g.DrawString(lastS, smallFont, textBrush, 27, lastY);
							lastHH = lastS = smallText;
							lastY = rt2.Top+delta+1;
							if (shortHour && time.Minutes==0 && time.Seconds==0) 
							{
								lastHH = (0<=time.Hours && time.Hours<12) ? "am" : "pm";
								if (time.Hours%12==0)
									lastS = lastHH;
							}
						}
					}
					else if (isHalf) 
					{
						g.DrawString(smallText, smallFont, textBrush, 27, rt2.Top+3);
					}
				}
				if (lastHH!=null)
					g.DrawString(lastHH, smallFont, textBrush, 27, lastY);

				TimeSpan time2 = FirstHour+CalendarTools.Mul(mins, hourItemCount);
				bool isHour2 = time2.Minutes==0;

				g.DrawLine(linePen, isHour2 ? 0 : 29, Height-1, hoursLabelWidth-5, Height-1);

				CalendarTools.FillRectangle(g, rt, HoursLabelColor, HoursLabelColorSecond, false);
			}
		}

		#endregion // PrintHoursLabel

		#region - PrintHoursWorkspace -

		/// <summary>
		/// Prints the hours workspace.
		/// </summary>
		/// <param name="g">The g.</param>
		private void PrintHoursWorkspace(Graphics g) 
		{
			Rectangle rect = GetPrintRegionRectangle(Regions.HoursWorkspace);
			if (rect.Width<=0 || rect.Height<=0) return;

			using (Brush selectedBrush = new SolidBrush(SelectedColor))
			using (Pen linePen = new Pen(DayLineColor, 1))
			using (Pen linePenDots = new Pen(DayLineColor, 1)) 
			{
				linePenDots.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

				DateTime date = PrintDate;
				GetDayInfoEventArgs gdi = null;
				GetDayInfoEventArgs.ResourceInfo dayRi = null;
				for (int resourceIndex = 0; resourceIndex<Resources.Count; ++resourceIndex) 
				{
					Rectangle dayRt = new Rectangle(hoursLabelWidth + resourceIndex*ColumnPrintWidth, 
						2*daysLabelHeight, ColumnPrintWidth,
						PrintHourItemCount * hourTaskHeight);
					g.DrawLine(linePen, dayRt.Right-1, dayRt.Top, dayRt.Right-1, dayRt.Bottom);
					Resource res = resourceIndex>=Resources.Count ? null : Resources[resourceIndex];

					if (gdi==null || gdi.Date!=date) 
					{
						gdi = new GetDayInfoEventArgs(this, date);
						OnGetDayInfo(gdi);
						dayRi = new GetDayInfoEventArgs.ResourceInfo(gdi);
					}
					GetDayInfoEventArgs.ResourceInfo ri = null;
					if(gdi.resourcesInfo!=null)
						ri = gdi.resourcesInfo[resourceIndex];
					
					//all hours are work, below - just free hours
					CalendarTools.FillRectangle(g, dayRt, gdi.WorkHourColor, gdi.WorkHourColorSecond, true);

					///////////////////////////////////////////////////////////
					// Calculate a BusyColor
					// 
					Color busyColor = SystemColors.Window;
					try
					{
						for (int i = CurrentDays.Count; --i>=0; ) 
						{
							Appointment ap = (Appointment)CurrentDays[i];
							if (ap.dateStart<=date && date<ap.dateStop && ap.BusyColor!=Color.Empty && ap.ResourceIndex==resourceIndex) 
							{
								busyColor = ap.BusyColor;
								break;
							}
						}
					}
					catch(Exception)
					{
					}
					Color resColor = Color.Empty;
					bool isResColor = res!=null && res.BackColor!=Color.Empty;
					if (isResColor)
						resColor = res.BackColor;
					else if (ri!=null)
						resColor = ri.WorkHourColor;

					if (ri==null) ri = dayRi;

					using (Brush extraBrush = new SolidBrush(ExtraBreakHourColor))
					using (Brush freeBrush = new SolidBrush(ri.FreeHourColor))
					using (Brush busyBrush = new SolidBrush(busyColor)) 
					using (Brush resBrush = resColor==Color.Empty ? null : new SolidBrush(resColor)) 
					{
						TimeSpan mins = minsResolution;
						for (int i = PrintHourItemCount; --i>=0; ) 
						{
							TimeSpan time = StartPrintTime+CalendarTools.Mul(mins, i);
							DateTime dateTime = date.Add(time);
							bool work = true;
							bool extra = false;
							if (ri!=null)
							{
								work = ri.WorkHourBegin<=time && time<ri.WorkHourEnd; //is it work time or not
								TimeSpan t = time + new TimeSpan(0,1,0,0);								
								if(ri.Breaks.IsBreak(time, t)!=time)
								{
									work = false;
									if(ri.Breaks.IsExtraBreak(time, t))
										extra = true;
									else
										extra = false;
								}
							}
							else
							{
								work = gdi.WorkHourBegin<=time && time<gdi.WorkHourEnd;//is it work time or not
								TimeSpan t = time + new TimeSpan(0,1,0,0);								
								if(ri.Breaks.IsBreak(time, t)!=time)
								{
									work = false;
									if(ri.Breaks.IsExtraBreak(time, t))
										extra = true;
									else
										extra = false;
								}
							}
							if (work && gdi.FreeDay)
								work = false;//is it work time or not

							Rectangle rt = GetHourPrintRectangle(time, true, true);
							rt.Intersect(dayRt);

							bool paint = true;
							Brush backBrush = freeBrush;
							if (resBrush!=null && (work || isResColor))
								backBrush = resBrush;
							else if (extra)
								backBrush = extraBrush;
							else if (work)
								paint = false;
							if (paint)
								g.FillRectangle(backBrush, rt); //not work time

							bool isHour = time.Minutes==0;
							g.DrawLine(isHour ? linePen : linePenDots, 
								rt.Left, rt.Bottom-1, rt.Right, rt.Bottom-1);
						}

						g.FillRectangle(busyBrush, new Rectangle( //between columns
							dayRt.Location, new Size(markWidth, dayRt.Height)));
						g.DrawLine(linePen, dayRt.Left, dayRt.Top, dayRt.Left, dayRt.Bottom);
						g.DrawLine(linePen, dayRt.Left+markWidth, dayRt.Top, dayRt.Left+markWidth, dayRt.Bottom);
					}
				}

				foreach (Appointment ap in CurrentHours)
				{
					InvokePrintAppointment(new PaintAppointmentEventArgs(g, ap));
				}
			}
		}

		#endregion //	PrintHoursWorkspace

		#region - InvokePrintAppointment -
		/// <summary>
		/// Invokes the print appointment.
		/// </summary>
		/// <param name="args">The <see cref="ComponentGo.Calendars.PaintAppointmentEventArgs"/> instance containing the event data.</param>
		internal void InvokePrintAppointment(PaintAppointmentEventArgs args) 
		{
			OnPaintAppointment(args);
			if (!args.UserPaint)
				Print(args.Graphics, args.Appointment);
		}
		#endregion // InvokePrintAppointment

		#region - Start/Finish Printing -

		//private bool printing = false;

		/// <summary>
		/// Prepare the control for printing.
		/// </summary>
		private void StartPrinting()
		{
			//printing = true;
		}

		/// <summary>
		/// Finishing control printing.
		/// </summary>
		private void FinishPrinting()
		{
			printCanvasRectangle = Rectangle.Empty;
			//printing = false;
		}
		#endregion

		#endregion //Printing Process

		#region + PrintRectangles - 

		#region - PrintCanvasRectangle -
		/// <summary>
		/// Gets the rectangle for printing canvas.
		/// </summary>
		public override Rectangle PrintCanvasRectangle
		{
			get
			{
				if(printCanvasRectangle == Rectangle.Empty)
				{
					int width = 0;
					int height = 0;
					
					width = hoursLabelWidth + Resources.Count * ColumnPrintWidth;
					height = 2*daysLabelHeight + PrintHourItemCount*hourTaskHeight;


					printCanvasRectangle = new Rectangle(0, 0, width, height);
				}
				return printCanvasRectangle;
			}
		}
		private Rectangle printCanvasRectangle = Rectangle.Empty;

		#endregion

		#region - GetPrintRegionRectangle  -
		/// <summary>
		/// Calculates a rectangle covering the selected region of a control in printing mode.
		/// </summary>
		/// <param name="region">Region, the rectangle is calculated for.</param>
		/// <returns>Rectangle calculated relative to a calendar control.</returns>
		public Rectangle GetPrintRegionRectangle(Regions region) 
		{
			switch (region) 
			{
				case Regions.HoursLabel:
					return new Rectangle(
						Point.Empty.X, Point.Empty.Y , 
						hoursLabelWidth, PrintHourItemCount*hourTaskHeight + 2*daysLabelHeight);

				case Regions.DaysLabel:
					return new Rectangle(
						hoursLabelWidth, 0,
						Resources.Count * ColumnPrintWidth, daysLabelHeight);

				case Regions.ResourcesLabel:
					if (isResourceBar)
						return new Rectangle(
							hoursLabelWidth, daysLabelHeight,
							Resources.Count * ColumnPrintWidth, daysLabelHeight);
					return Rectangle.Empty;

				case Regions.HoursWorkspace: 
				{
					return new Rectangle(
						new Point(hoursLabelWidth, 0), 
						new Size(Resources.Count * ColumnPrintWidth, PrintHourItemCount*hourTaskHeight));
				}

				case Regions.DaysWorkspace:
					return AllDayVisible ? getDaysWorkspaceRt() : Rectangle.Empty;
			}	
			throw new ArgumentException("Unexpected value", "region");		
		}
		#endregion //GetPrintRegionRectangle

		#region - GetHourPrintRectangle -
		/// <summary>
		/// Gets the hour print rectangle.
		/// </summary>
		/// <param name="hour">The hour.</param>
		/// <param name="inner">if set to <c>true</c> [inner].</param>
		/// <param name="roundToRes">if set to <c>true</c> [round to res].</param>
		/// <returns></returns>
		private Rectangle GetHourPrintRectangle(TimeSpan hour, bool inner, bool roundToRes) 
		{
			hour -= StartPrintTime;
			if (inner && hour<TimeSpan.Zero) return Rectangle.Empty;
			int mins = (int)CalendarTools.Div(hour, minsResolution);

			return new Rectangle(
				0, mins*hourTaskHeight + 2*daysLabelHeight, 
				hoursLabelWidth + Resources.Count * ColumnPrintWidth, hourTaskHeight);
		}
		#endregion //GetHourPrintRectangle

		#region - PrintHourItemCount -
		/// <summary>
		/// Gets the print hour item count.
		/// </summary>
		/// <value>The print hour item count.</value>
		private int PrintHourItemCount 
		{
			get 
			{
				return (int)CalendarTools.Div(
					FinishPrintTime.Subtract(StartPrintTime), 
					minsResolution);
			}
		}

		#endregion //PrintHourItemCount

		#region - PrintWidth -
		/// <summary>
		/// Gets the width of the print.
		/// </summary>
		/// <value>The width of the print.</value>
		private int PrintWidth
		{
			get
			{
				return clientWidth;
			}
		}
		#endregion // PrintWidth

		#region - GetHourTopPrintPoint -
		/// <summary>
		/// Gets the hour top print point.
		/// </summary>
		/// <param name="hour">The hour.</param>
		/// <returns></returns>
		private int GetHourTopPrintPoint(TimeSpan hour) 
		{
			hour -= StartPrintTime;
			double mins = CalendarTools.Div(hour, minsResolution);

			return (int)mins*hourTaskHeight-1;
		}
		#endregion //GetHourTopPrintPoint

		#region - ColumnPrintWidth -
		/// <summary>
		/// Gets the width of the column print.
		/// </summary>
		/// <value>The width of the column print.</value>
		private int ColumnPrintWidth
		{
			get {return 150;}
		}
		#endregion // ColumnPrintWidth

		#region - GetAppointmentPrintRectangles -
		/// <summary>
		/// Gets the appointment print rectangles.
		/// </summary>
		/// <param name="ap">The ap.</param>
		/// <returns></returns>
		private Rectangle [] GetAppointmentPrintRectangles(Appointment ap) 
		{
			int firstCol = ap.ResourceIndex;
			DateTime firstDate = PrintStartDate;
			Rectangle [] rts = new Rectangle[1];
			if (rts.Length>0) 
			{
				int startCol = ap.ResourceIndex;
				int stopCol = ap.ResourceIndex;

				Rectangle workRt = GetPrintRegionRectangle(Regions.HoursWorkspace);

				int apIdx = CurrentHours.IndexOf(ap);
				if (apIdx<0) return new Rectangle[0];

				for (int i = 0; i<rts.Length; ++i) 
				{
					DateTime d1 = firstDate.AddDays(i);
					if (d1<ap.dateStart) d1 = ap.dateStart;
					TimeSpan t1 = d1.TimeOfDay;

					DateTime d2 = firstDate.AddDays(i+1);
					if (d2>ap.dateStop.AddMinutes(-1)+minsResolution) 
						d2 = ap.dateStop.AddMinutes(-1)+minsResolution;
					TimeSpan t2 = d2.TimeOfDay;
					if (t2==TimeSpan.Zero) t2 = TimeSpan.FromDays(1);

					Rectangle rt = Rectangle.Empty;
					if (t1<t2) 
					{
						Rectangle rt1 = GetHourPrintRectangle(t1, false, true);
						if (rt1!=Rectangle.Empty) 
						{
							Rectangle rt2 = GetHourPrintRectangle(t2, false, true);
							if (rt1!=Rectangle.Empty) 
							{
								rt = getColumnsRectangle(workRt, firstCol, firstCol+1);

								int w = rt.Width/Math.Max(itemsHours[apIdx].Count, 1);
								rt.X += w*itemsHours[apIdx].Index;
								rt.Width = w-4;
								rt.Y = rt1.Top-1;
								rt.Height = rt2.Top-rt1.Top;
							}
						}
					}
					//if (ap.Selected) 
					//{
					//	if (firstCol==startCol) 
					//	{
					//		rt.Y -= markWidth;
					//		rt.Height += markWidth;
					//	}
					//	if (firstCol==stopCol)
					//		rt.Height += markWidth;
					//}
					rts[i] = rt;
					firstCol += ResCount;
				}
			}
			return rts;
		}
		#endregion

		#endregion //PrintRectangles

		#region + Print Times -
	
		#region - StartPrintTime - 
		/// <summary>
		/// Gets the start print time.
		/// </summary>
		/// <value>The start print time.</value>
		protected TimeSpan StartPrintTime
		{
			get {return startPrintTime;}
		}
		private TimeSpan startPrintTime = TimeSpan.FromHours(8);
		#endregion //StartPrintTime

		#region - FinishPrintTime - 
		/// <summary>
		/// Gets the finish print time.
		/// </summary>
		/// <value>The finish print time.</value>
		protected TimeSpan FinishPrintTime
		{
			get {return finishPrintTime;}
		}
		private TimeSpan finishPrintTime  = TimeSpan.FromHours(20);
		#endregion //FinishPrintTime

		#region - PrintDate - 
		/// <summary>
		/// Gets the print date.
		/// </summary>
		/// <value>The print date.</value>
		public DateTime PrintDate
		{
			get
			{
				return SelectedDateBegin.Date;
			}
		}
		#endregion // PrintDate

		#region - PrintStartDate -
		/// <summary>
		/// Gets the print start date.
		/// </summary>
		/// <value>The print start date.</value>
		private DateTime PrintStartDate
		{
			get
			{
				return PrintDate.Add(StartPrintTime);
			}
		}
		#endregion //PrintStartDate

		#region - PrintFinishDate -
		/// <summary>
		/// Gets the print finish date.
		/// </summary>
		/// <value>The print finish date.</value>
		private DateTime PrintFinishDate
		{
			get
			{
				return PrintDate.Add(FinishPrintTime);
			}
		}
		#endregion //PrintFinishDate

		#endregion //Print Times

		#endregion //Custom Printing
	}
}
